大家都知道SimpleDateFormat是线程不安全的
1 protected Calendar calendar;
SimpleDateFormat中的calendar是成员变量,同实例多个线程下会共享该calendar对象
而在进行格式化的时候可能会由于第一个线程还没有格式化完成,而第二个线程已经将时间修改了的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private StringBuffer format (Date date, StringBuffer toAppendTo, FieldDelegate delegate) { 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; }
那么FastDateFormat为什么是线程安全的呢?首先FastDateFormat是有一个缓存的,在进行实例化的时候是通过cache缓存来获取实例的
1 2 3 4 5 6 7 8 9 10 private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() { @Override protected FastDateFormat createInstance (final String pattern, final TimeZone timeZone, final Locale locale) { return new FastDateFormat(pattern, timeZone, locale); } }; public static FastDateFormat getInstance (final String pattern) { return cache.getInstance(pattern, null , null ); }
将格式化格式、时区和国际化作为一个key存在了cInstanceCache中,cInstanceCache是一个ConcurrentHashMap,相当于相同的格式化格式、时区和国际化会使用同一个FastDateFormat实例
1 2 3 4 5 6 7 8 9 10 11 final MultipartKey key = new MultipartKey(pattern, timeZone, locale);F format = cInstanceCache.get(key); if (format == null ) { format = createInstance(pattern, timeZone, locale); final F previousValue= cInstanceCache.putIfAbsent(key, format); if (previousValue != null ) { format= previousValue; } }
而在使用FastDateFormat进行格式化的时候,是在方法中定义的Calendar局部变量,是不会出现线程安全问题的
1 2 3 4 5 public String format (final Date date) { final Calendar c = newCalendar(); c.setTime(date); return applyRulesToString(c); }