SimpleDateFormat的线程安全问题
在多个线程并发的情况下,同时需要用到SimpleDateFormat的情况下会出现很多问题,会发生线程挂掉,日期转行异常等
原因
在JDK的API中有关于SimpleDateFormat的说明:Synchronization
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不能进行线程同步,也就是不具备线程安全,官方建议在多线程情况下,不要单独的使用SimpleDateFormat,如果必须要债多线程情况下使用,一定要进行同步synchronized;
分析
这也难怪,往往只顾及SimpleDateFormat的好用,但却没注意这些东西,会犯一些小错误。警戒
源码解析
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);//我们传入的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;
}
public abstract class DateFormat extends Format {
/**
* The {@link Calendar} instance used for calculating the date-time fields
* and the instant of time. This field is used for both formatting and
* parsing.
*
* <p>Subclasses should initialize this field to a {@link Calendar}
* appropriate for the {@link Locale} associated with this
* <code>DateFormat</code>.
* @serial
*/
protected Calendar calendar;
因为在SimpleDateFormat的父类DateFormat中,protected Calendar calendar;Calender类是关于时间和时区等的类,请注意它是一个成员变量,也就是说多个线程情况下,第一线程起来会改变它,还没结束是第二个线程也起来了,在第一个线程结束之前却也改变他了,有可能第一个线程因为第二个线程的改变而去执行第二个线程的结果。这就离我们预期的结果大大不符合,故而发生线程挂掉,时间不对等错误。所以官方鼓励我们进行synchronized。
这也就是为什么SimpleDateFormat在多个线程试用下会发生同步问题
解决方案
在每个调用SimpleDateFormat的时,都new一个实例,也不会发生线程问题,但是会增加内存的开销,相对共享一个实例来说,只会浪费性能
解决方案Demo
/**
*
* 该类为多线程环境下的SimpleDateFormat工具类
* 解决多线程的并发问题
* SimpleDateFormat 的candler类 为多个线程公用,所以要线程同步
* @author shr
*
*/
public class ThreadSimpleDateFornat {
public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 解析Dtae,按照设置的时间日期格式返回字符串
* @param date
* @return
*/
public String format(Date date){
synchronized(dateFormat){
String format = dateFormat.format(date);
return format;
}
}
/**
* 解析包含时间的字符串,并且返回一个已经格式化的Date
* @param dateString
* @return
* @throws ParseException
*/
public Date parse(String dateString) throws ParseException{
synchronized(dateFormat){
return dateFormat.parse(dateString);
}
}
}
解决方案Demo2
思路:需要在乎那一点性能提升的,可以使用ThreadLocal作为容器,储存SipmleDateFormat
进行提升性能,但是那一点性能又有谁在乎呢