这就要分析下SimpleDateFormate为什么是线程不安全的了.
看SimpleDataFormat 源码时,会发现提到说SimpleDataFormat不是线程安全的。
* 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.
这是因为里面用了Calendar 这个成员变量来实现SimpleDataFormat,并且在Parse 和Format的时候对Calendar 进行了修改,calendar.clear(),calendar.setTime(date);
观察下面的parse方法
public Date parse(String text, ParsePosition pos)
{
int start = pos.index;
int oldStart = start;
int textLength = text.length();
calendar.clear(); // Clears all the time fields
boolean[] ambiguousYear = {false};
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:
if (start >= textLength || text.charAt(start) != (char)count)
{
pos.index = oldStart;
pos.errorIndex = start;
return null;
}
start++;
break;
case TAG_QUOTE_CHARS:
while (count-- > 0) {
if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {
pos.index = oldStart;
pos.errorIndex = start;
return null;
}
start++;
}
break;
default:
boolean obeyCount = false;
if (i < compiledPattern.length) {
int nextTag = compiledPattern[i] >>> 8;
if (!(nextTag == TAG_QUOTE_ASCII_CHAR || nextTag == TAG_QUOTE_CHARS)) {
obeyCount = true;
}
}
start = subParse(text, start, tag, count, obeyCount,
ambiguousYear, pos);
if (start < 0) {
pos.index = oldStart;
return null;
}
}
}
pos.index = start;
Date parsedDate;
try {
if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year
{
Calendar savedCalendar = (Calendar)calendar.clone();
parsedDate = calendar.getTime();
if (parsedDate.before(defaultCenturyStart))
{
// We can't use add here because that does a complete() first.
savedCalendar.set(Calendar.YEAR, defaultCenturyStartYear + 100);
parsedDate = savedCalendar.getTime();
}
}
else parsedDate = calendar.getTime();
}
catch (IllegalArgumentException e) {
pos.errorIndex = start;
pos.index = oldStart;
return null;
}
return parsedDate;
}
会发现有这样的调用
Date parse() {
calendar.clear(); // 清理calendar
...savedCalendar.set(Calendar.YEAR, defaultCenturyStartYear + 100); // 执行一些操作, 设置 calendar 的日期什么的
calendar.getTime(); // 获取calendar的时间
}
这里会导致的问题就是, 如果 线程A 调用了 sdf.parse(), 并且进行了 calendar.clear()后还未执行calendar.getTime()的时候,线程B又调用了sdf.parse(), 这时候线程B也执行了sdf.clear()方法, 这样就导致线程A的的calendar数据被清空了(实际上A,B的同时被清空了). 又或者当 A 执行了calendar.clear() 后被挂起, 这时候B 开始调用sdf.parse()并顺利i结束, 这样 A 的 calendar内存储的的date 变成了后来B设置的calendar的date
所以在多线程使用共享SimpleDateFormate的时候一定要注意线程安全。
要么采用synchronized 要么采用ThreadLocal本地线程变量来解决这个问题。
http://www.cnblogs.com/zemliu/archive/2013/08/29/3290585.html
这个blog上有详细的解决办法