SimpleDateFormat都知道是线程不安全的,在Java 8也出现了新的日期API,以后也不推荐使用SimpleDateFormat了。SimpleDateFormat线程不安全的原因也很简单,在format()方法中:

SimpleDateFormat线程安全问题

SimpleDateFormat线程安全问题

这个calendar变量被修改了,而这个calendar是一个共有的成员变量,在DateFormat中定义了一个protected属性的 Calendar类的对象:calendar。只是因为Calendar累的概念复杂,牵扯到时区与本地化等等,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。

SimpleDateFormat线程安全问题

而在后面的subFormat()方法中很多地方都使用到了calendar:

SimpleDateFormat线程安全问题

而一般我们使用SimpleDateFormat都是将其抽取成为一个公共的日期工具类,也就是说SimpleDateFormat对象本身是共有的,但是这个对象在调用方法的时候确对自身属性进行了修改,这在多线程环境下肯定会出现线程安全问题。

下面来演示一个示例:

package com.example.demoClient.thread;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Dongguabai
 * @date 2018/11/2 17:50
 */
public class MyThread extends Thread{

    private SimpleDateFormat sdf;
    private String dateString;

    public MyThread(SimpleDateFormat sdf, String dateString) {
        this.sdf = sdf;
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try {
            Date dateRef = sdf.parse(dateString);
            String newDateString = sdf.format(dateRef);
            if (!newDateString.equals(dateString)){
                System.out.println("ThreadName="+this.getName()+"报错了  日期字符串:"+dateString+"转换成的日期为:"+newDateString);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

测试代码:

package com.example.demoClient.thread;

import java.text.SimpleDateFormat;

/**
 * @author Dongguabai
 * @date 2018/11/2 17:58
 */
public class Test {

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        String[] dateStringArray = new String[]{"2000-01-01","2000-01-02","2000-01-03","2000-01-04","2000-01-05","2000-01-06","2000-01-07","2000-01-08","2000-01-09","2000-01-10"};
        MyThread[] threadArray = new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threadArray[i] = new MyThread(sdf,dateStringArray[i]);
        }
        for (int i = 0; i < 10; i++) {
            threadArray[i].start();
        }
    }
}

运行结果:

SimpleDateFormat线程安全问题

使用单例的SimpleDateFormat出现了线程安全问题。

解决方案一

每次调用都new一个SimpleDateFormat。

改动也很简单:

SimpleDateFormat线程安全问题

运行结果:

SimpleDateFormat线程安全问题

控制台没有任何输出,运行正常。

解决方案二

使用ThreadLocal。每次获取SimpleDateFormat都从ThreadLocal中获取。也就是说在DateUtil中维护一个ThreadLocal<SimpleDateFormat>即可,每次直接从ThreadLocal中get即可。

 

参考资料:

https://www.cnblogs.com/zhuimengdeyuanyuan/archive/2017/10/25/7728009.html

分类:

技术点:

相关文章: