SimpleDateFormat
is simple in use, but yet complicated. Developer generally writes code and test as a single user, and succeeds. but in case of parallel and concurrent requests,SimpleDateFormat
is a complicated dateformat.
having exposed as static in service or controller layer, and when multiple threads can access at same time, it can behave abnornally.
DateFormat APi class also recommends, "It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally."
code to demontsrate it's failure in multi threading environment.
public final class DateUtils {
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private DateUtils() {
}
public static Date parse(String target) throws ParseException {
return DATE_FORMAT.parse(target);
}
public static String format(Date target) {
return DATE_FORMAT.format(target);
}
private static void testSimpleDateFormatWithThreads() {
ExecutorService executorService = Executors.newFixedThreadPool(5);
final String source = "2019-01-11";
IntStream.rangeClosed(0, 20)
.forEach((i) -> executorService.submit(() -> {
try {
System.out.println(DateUtils.parse(source));
} catch (ParseException e) {
e.printStackTrace();
}
}));
executorService.shutdown();
}
public static void main(String[] args) {
testSimpleDateFormatWithThreads();
}
}
Output - This varies from machine to machine and will generate difefrent output how many time you run.
Wed Nov 11 00:00:00 IST 2201
Wed Nov 11 00:00:00 IST 2201
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Mon Dec 31 00:00:00 IST 2018
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Fri Jan 11 00:00:00 IST 2019
Sun Jan 11 00:00:00 IST 2201
Fri Jan 11 00:00:00 IST 2019
This breaks due to Calendar instance variable inside DateFormat
-> SimpleDateFormat
class.
Solution
Using ThreadLocal
public static final ThreadLocal SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
Java 8, DateTimeFormatter
API
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");