线程不安全SimpleDateFormat的替换方案DateFormatUtils、DateUtils

1、最近生产环境遇到了一个问题:会员查询接口,会员返回的开始时间竟然比过期时间大。
vipStart=2018-08-07 18:37:28 vipEnd=2018-08-05 13:29:54

2、会员的开始时间是根据会员的过期时间来计算的(往前推31天)
查看系统日志,排除了数据库、代码的计算可能性后,决定用2018-08-07 18:37:28去查询一下日志,是否是线程切换造成的。
后面查询到了一个线程日志
vipStart=2018-07-07 18:37:28 vipEnd=2018-08-07 18:37:28

3、明显是线程不安全问题,后面查询了格式化工具:
public class DateUtils {
private static SimpleDateFormat simpleDateFormat = null;
method1(){}
method2(){}
method3(){}
}

(1)可以看到工具栏中static SimpleDateFormat是线程不安全的,当使用不同的pattern进行修改时,产生的日期格式可能不一样的。
(2)SimpleDateFormat,类内部有一个Calendar对象引用,它用来储存和这个sdf相关的日期信息,例如sdf.parse(dateStr), sdf.format(date) 诸如此类的方法参数传入的日期相关String, Date等等, 都是交由Calendar引用来储存的.这样就会导致一个问题,如果你的sdf是个static的, 那么多个thread 之间就会共享这个SimpleDateFormat, 同时也是 共享这个Calendar引用, 并且, 观察 sdf.format() 方法,你会发现有如下的调用: 
   public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

    // 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++];
            }

4、替代方案:

(1)将工具类中private static SimpleDateFormat simpleDateFormat = null;去除掉,写到方法里面去;

(2)使用threadlocal方式;

(3)使用Apache下面的DateFormatUtils、DateUtils、最主要的区别是线程安全、占用内存少。(推荐)

import org.apache.commons.lang.time.DateFormatUtils;

import org.apache.commons.lang.time.DateUtils;

附图:SimpleDateFormat与DateFormatUtils、DateUtils占用的内存,可以看出apache工具类的优势。

线程不安全SimpleDateFormat的替换方案DateFormatUtils、DateUtils_第1张图片



你可能感兴趣的:(线程不安全SimpleDateFormat的替换方案DateFormatUtils、DateUtils)