[ACCEPTED]-Synchronizing access to SimpleDateFormat-simpledateformat

Accepted answer
Score: 44
  1. Creating SimpleDateFormat is expensive. Don't use 11 this unless it's done seldom.

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

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

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

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

Score: 25

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

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

Score: 20

If you are using Java 8, you may want to 1 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);
Score: 6

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

Score: 4

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

Score: 3

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

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

Score: 1

Another option is to keep instances in a 12 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 11 should be something close to the estimated 10 number of threads which can routinely call 9 this function at the same time. In the worst 8 case where more threads than this number 7 do actually use all the instances concurrently, some 6 SimpleDateFormat instances will be created 5 which cannot be returned to dateFormatQueue 4 because it is full. This will not generate 3 an error, it will just incur the penalty 2 of creating some SimpleDateFormat which 1 are used only once.

Score: 1

I just implemented this with Option 3, but 4 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 3 set locale and time zone unless you really 2 want the default settings (defaults are 1 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);
    }
    

More Related questions