Synchronizing access to SimpleDateFormat

JavaMultithreadingConcurrencySimpledateformat

Java Problem Overview


The javadoc for SimpleDateFormat states that SimpleDateFormat is not synchronized.

> "Date formats are not synchronized. It > is recommended to create separate > format instances for each thread. If > multiple threads access a format > concurrently, it must be synchronized > externally."

But what is the best approach to using an instance of SimpleDateFormat in a multi threaded environment. Here are a few options I have thought of, I have used options 1 and 2 in the past but I am curious to know if there are any better alternatives or which of these options would offer the best performance and concurrency.

Option 1: Create local instances when required

public String formatDate(Date d) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    return sdf.format(d);
}

Option 2: Create an instance of SimpleDateFormat as a class variable but synchronize access to it.

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date d) {
    synchronized(sdf) {
        return sdf.format(d);
    }
}

Option 3: Create a ThreadLocal to store a different instance of SimpleDateFormat for each thread.

private ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();
public String formatDate(Date d) {
    SimpleDateFormat sdf = tl.get();
    if(sdf == null) {
        sdf = new SimpleDateFormat("yyyy-MM-hh");
        tl.set(sdf);
    }
    return sdf.format(d);
}

Java Solutions


Solution 1 - Java

  1. Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.

  2. OK if you can live with a bit of blocking. Use if formatDate() is not used much.

  3. Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.

For applications both 2. and 3. are viable options. Which is best for your case depends on your use case. Beware of premature optimization. Only do it if you believe this is an issue.

For libraries that would be used by 3rd party I'd use option 3.

Solution 2 - Java

The other option is Commons Lang FastDateFormat but you can only use it for date formatting and not parsing.

Unlike Joda, it can function as a drop-in replacement for formatting. (Update: Since v3.3.2, FastDateFormat can produce a FastDateParser, which is a drop-in thread-safe replacement for SimpleDateFormat)

Solution 3 - Java

If you are using Java 8, you may want to use java.time.format.DateTimeFormatter:

> This class is immutable and thread-safe.

e.g.:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String str = new java.util.Date().toInstant()
                                 .atZone(ZoneId.systemDefault())
                                 .format(formatter);

Solution 4 - Java

Commons Lang 3.x now has FastDateParser as well as FastDateFormat. It is thread safe and faster than SimpleDateFormat. It also uses the same format/parse pattern specifications as SimpleDateFormat.

Solution 5 - Java

Don't use SimpleDateFormat, use joda-time's DateTimeFormatter instead. It is a bit stricter in the parsing side and so isn't quite a drop in replacement for SimpleDateFormat, but joda-time is much more concurrent friendly in terms of safety and performance.

Solution 6 - Java

I would say, create a simple wrapper-class for SimpleDateFormat that synchronizes access to parse() and format() and can be used as a drop-in replacement. More foolproof than your option #2, less cumbersome than your option #3.

Seems like making SimpleDateFormat unsynchronized was a poor design decision on the part of the Java API designers; I doubt anyone expects format() and parse() to need to be synchronized.

Solution 7 - Java

Another option is to keep instances in a thread-safe queue:

import java.util.concurrent.ArrayBlockingQueue;
private static final int DATE_FORMAT_QUEUE_LEN = 4;
private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
private ArrayBlockingQueue<SimpleDateFormat> dateFormatQueue = new ArrayBlockingQueue<SimpleDateFormat>(DATE_FORMAT_QUEUE_LEN);
// thread-safe date time formatting
public String format(Date date) {
    SimpleDateFormat fmt = dateFormatQueue.poll();
    if (fmt == null) {
        fmt = new SimpleDateFormat(DATE_PATTERN);
    }
    String text = fmt.format(date);
    dateFormatQueue.offer(fmt);
    return text;
}
public Date parse(String text) throws ParseException {
    SimpleDateFormat fmt = dateFormatQueue.poll();
    if (fmt == null) {
        fmt = new SimpleDateFormat(DATE_PATTERN);
    }
    Date date = null;
    try {
        date = fmt.parse(text);
    } finally {
        dateFormatQueue.offer(fmt);
    }
    return date;
}
        

The size of dateFormatQueue should be something close to the estimated number of threads which can routinely call this function at the same time. In the worst case where more threads than this number do actually use all the instances concurrently, some SimpleDateFormat instances will be created which cannot be returned to dateFormatQueue because it is full. This will not generate an error, it will just incur the penalty of creating some SimpleDateFormat which are used only once.

Solution 8 - Java

I just implemented this with Option 3, but made a few code changes:

  • ThreadLocal should usually be static

  • Seems cleaner to override initialValue() rather than test if (get() == null)

  • You may want to set locale and time zone unless you really want the default settings (defaults are very error prone with Java)

     private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() {
         @Override
         protected SimpleDateFormat initialValue() {
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US);
             sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
             return sdf;
         }
     };
     public String formatDate(Date d) {
         return tl.get().format(d);
     }
    

Solution 9 - Java

Imagine your application has one thread. Why would you synchronize access to SimpleDataFormat variable then?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question3urdochView Question on Stackoverflow
Solution 1 - JavaPeter KnegoView Answer on Stackoverflow
Solution 2 - JavaAdam GentView Answer on Stackoverflow
Solution 3 - JavaPaul VargasView Answer on Stackoverflow
Solution 4 - JavaChasView Answer on Stackoverflow
Solution 5 - JavaJed Wesley-SmithView Answer on Stackoverflow
Solution 6 - JavaAndyView Answer on Stackoverflow
Solution 7 - JavaSamJView Answer on Stackoverflow
Solution 8 - JavamwkView Answer on Stackoverflow
Solution 9 - JavaVortexView Answer on Stackoverflow