I want to make following code thread safe. What is the best way to achieve it?
private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(
Better, use: org.apache.commons.lang.time.FastDateFormat (is a fast and thread-safe version of SimpleDateFormat)
The troublesome old date-time classes bundled with the earliest versions of Java have been supplanted by the java.time classes. The java.time classes are thread-safe and use immutable objects.
Replace your formatter and date types with java.time types to automatically get thread-safety.
Define your DateTimeFormatter globally if so desired. That class can automatically localize the string being generated, or you can specify a certain format.
ZoneId
for a time zone in which to adjust the moment.The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds. The ZonedDateTime class adjusts an Instant
into a particular time zone.
Your code, translated to java.time classes. In real work I would break this out into multiple lines, and would trap for exceptions.
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ;
private static final ZoneId ZONE_ID = ZoneId.of( "America/Montreal" );
public static final String eventTypeToDateTimeString(long timestamp)
{
return Instant.ofEpochMilli( timestamp ).atZone( ZONE_ID ).format( DATE_TIME_FORMATTER );
}
I do not advise passing around a long
as a way of representing date-time values. Makes debugging and logging difficulty as a human cannot discern the meaning of the date-time value. Instead, pass around the java.time types such as Instant. Using the java.time types provides type-safety and makes your code more self-documenting.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
There are several alternatives, with different trade-offs.
You can synchronize access to a single DateFormat. This minimizes the number of formatter objects created, but different threads will have to contend for a lock before they can access the formatter. This is probably the worst alternative performance-wise; a lot of threads could end up spending time waiting, and the more threads you have the worse this will be.
You can create a new DateFormat object for each use. That will eliminate contention between threads, but if there is a lot of date formatting you could put pressure on the garbage collector with this approach, and that will hurt performance. But this can work well enough in many cases and is very simple.
A third alternative, making the DateFormat a threadlocal variable, is a lot more efficient. There is no contention between threads, and the formatter can be reused by a thread repeatedly, so it doesn't create nearly so much garbage. The downsides would be that it's the least straightforward approach, and any objects you put in a threadLocal may stick around longer than you want if you don't clear them out.
The simplest solution would be:
synchronized public static final String eventTypeToDateTimeString(long timestamp) {
return DATE_FORMAT.format(new Date(timestamp));
}
No need to use ThreadLocal
s since this is all in a static context.
You can
Create a new DateFormat
instance every time you need one.
Use a synchronized
block, as pointed by @Giovanni Botta.
Use ThreadLocal
:
private static final ThreadLocal<DateFormat> THREADLOCAL_FORMAT =
new ThreadLocal<DateFormat>() {
@Override protected DateFormat initialValue() {
return DateFormat.getDateTimeInstance();
}
};
public static final String eventTypeToDateTimeString(long timestamp) {
return THREADLOCAL_FORMAT.get().format(new Date(timestamp));
}
Actually, using ThreadLocal
might give you the best performance if you have a thread pool (meaning threads are reused), which most web containers do.
http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html
Just create a new copy on each call until it's actually demonstrated to be a performance problem. The overhead of manually managing thread-locals is likely to swamp any advantage you get from caching them.