在java中,格式化和解析时间我们常常会用到SimpleDateFormat这个类,但是这个类是线程不安全的,也即是说我们不能为了方便或者为了减小频繁创建SimpleDateFormat对象带来的内存开销而将其设置为类的静态变量供多个线程共享使用。
我们跟进SimpleDateFormat的format方法观察一下线程不安全的原因,format方法最终调用的代码如下:
// Called from Format after creating a FieldDelegate 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; }
可以看到方法的第一行就将传入的入参date表示的时间转成了Calendar类型,真正的日期格式化的操作在subFormat方法中使用calendar来获取时间(鉴于这个方法是真的长而且复杂,我就不研究也不粘贴了)。
假设我们的SimpleDateFormat对象是多个线程共享的,那么A线程刚设置了calendar的值,但是此时A线程还没有完成对时间的格式化操作,这时B线程过来了,由于这部分代码是没有加锁相关的操作,所以calendar的值会被B线程修改。这就导致了A线程最终格式化出来的时间是错误的。也就导致了线程不安全。