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.
那么,原因又是什么呢?我们来简单地看一下 SimpleDateFormat 的源码:
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate){// Convert input date to time field list calendar.setTime(date);boolean useDateFormatSymbols = useDateFormatSymbols();for (int i = 0; i < compiledPattern.length; ) {int tag = compiledPattern[i] >>> 8;int count = compiledPattern[i++] & 0xff;if (count == 255) { count = compiledPattern[i++] << 16; count |= compiledPattern[i++]; }switch (tag) {case TAG_QUOTE_ASCII_CHAR: toAppendTo.append((char)count);break;case TAG_QUOTE_CHARS: toAppendTo.append(compiledPattern, i, count); i += count;break;default: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);break; } }return toAppendTo;}
Message 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.
So officially, no - it's not thread-safe.
The docs for SimpleDateFormat say much the same thing.
Now, the docs may just be being conservative, and in practice it'll work just fine in multiple threads, but it's not worth the risk.
http://jeremymanson.blogspot.com/2010/01/note-on-thread-unsafety-of-format.html If you create a Format object (or a MessageFormat, NumberFormat, DecimalFormat, ChoiceFormat, DateFormat or SimpleDateFormat object), it cannot be shared among threads. The above code does not share its SimpleDateFormat object among threads, so it is safe.
If the formatter field had been a static field of the class, the method would not be thread-safe. However, this way, you have to create a (relatively) expensive SimpleDateFormat object every time you invoke the method. There are many ways around this. One answer (if you want to avoid locking) is to use a ThreadLocal:
private static final ThreadLocal formatters =
new ThreadLocal() {
@Override public SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
}
};
public static boolean getDate(String str, String str2) {
SimpleDateFormat formatter = formatters.get();
...
SimpleDateFormat stores intermediate results in instance fields. So if one instance is used by two threads they can mess each other's results.
Looking at the source code reveals that there is a Calendar instance field, which is used by operations on DateFormat / SimpleDateFormat
For example parse(..) calls calendar.clear() initially and then calendar.add(..). If another thread invokes parse(..) before the completion of the first invocation, it will clear the calendar, but the other invocation will expect it to be populated with intermediate results of the calculation.
To be honest, I don't understand why they need the instance field, but that's the way it is.
They don't need the instance field; it is undoubtedly the result of sloppy programming in a misguided attempt at efficiency.
due to lack of synchronization on the DateFormat class. Typical exceptions thrown when parsing to create a Date object are :
java.lang.NumberFormatException
java.lang.ArrayIndexOutOfBoundsException
using the ThreadLocal approach without Thread pools, is equivalent to using the “getDateInstance(..)” approach due to the fact that every newThread has to initialize its local DateFormat instance prior using it, thus a new DateFormatinstance will be created with every single execution.
A week year is in sync with a WEEK_OF_YEAR cycle. All weeks between the first and last weeks (inclusive) have the same week year value. Therefore, the first and last days of a week year may have different calendar year values.