美文网首页
SimpleDateFormat 线程安全的问题以及解决方案

SimpleDateFormat 线程安全的问题以及解决方案

作者: ggr | 来源:发表于2018-06-30 13:39 被阅读0次

之前在一本书上看到一个比较有意思的问题如下:

public class MyThread extends Thread {

    private SimpleDateFormat dateFormat;

    private String dateString;

    MyThread(){
        super();
    }
    MyThread(SimpleDateFormat dateFormat,String dateString){
        super();
        this.dateFormat =dateFormat;
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try{
            java.util.Date dateStr = dateFormat.parse(dateString);
            String newDateString = dateFormat.format(dateStr);
            if(!newDateString.equals(dateString)){
                System.out.println("时间转换出错了dateString="+dateString+"newDateString="+newDateString);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
public class FormatErrorTest {
    public static void main(String[] args) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStrings = new String[]{"2018-08-01","2018-08-02","2018-08-03","2018-08-04","2018-08-05","2018-08-06","2018-08-07","2018-08-08"};
        MyThread[] myThreads = new MyThread[dateStrings.length];

        for(int i=0;i<dateStrings.length;i++){
            myThreads[i] = new MyThread(dateFormat,dateStrings[i]);
        }

        for(int i=0;i<dateStrings.length;i++){
            myThreads[i].start();
        }
    }
}
  • 思考这个main方法执行会不会出现问题?
    运行结果如下:


    image.png

其实SimpleDateFormat 是线程不安全。我自己看了一下源代码
其实SimpleDateFormat 这个类我们主要用的还是他的parse方法和format方法。
部分源码如下:

public Date parse(String text, ParsePosition pos){
         //...
 }

//StringBuffer 虽然是线程安全的,但是不能保证在参数传入阶段的线程同步问题,只能保证其每次只能同时被一个线程使用。
public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos){
        //...
 }

这2个方法明显没有做线程安全的控制,所以有线程安全问题也是可以理解的。如果线程A正在使用这个实例那个这个时候线程B又传入了新的时间字符串将会导致线程A得到的结果实际上可能是线程B需要的结果。

对于SimpleDateFormat这个类,建议在使用的时候一个线程一个实例,不要当做共享资源来使用,除非你显示了对这个类的实例的获取进行了特殊的线程安全处理。目前有2种比较通用的方法来解决这个问题。如下:
方法一,一个线程一个SimpleDateFormat实例

public class FormatErrorTest {
    public static void main(String[] args) {
       
        String[] dateStrings = new String[]{"2018-08-01","2018-08-02","2018-08-03","2018-08-04","2018-08-05","2018-08-06","2018-08-07","2018-08-08"};
        MyThread[] myThreads = new MyThread[dateStrings.length];
        for(int i=0;i<dateStrings.length;i++){
           SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            myThreads[i] = new MyThread(dateFormat,dateStrings[i]);
        }
        for(int i=0;i<dateStrings.length;i++){
            myThreads[i].start();
        }
    }

运行结果如下:


image.png

方法二:使用ThreadLocal处理,本质上原理都是差不多,就是在使用的时候如果ThreadLocal上面没有就新建一个放进去,如果有就直接获取。

public class FormatErrorTest {

    static ThreadLocal<SimpleDateFormat> local = new ThreadLocal<SimpleDateFormat>();

    static SimpleDateFormat getSimpleDateFormat(){
        SimpleDateFormat dateFormat = null;
        dateFormat = local.get();
        if(dateFormat==null){
            System.out.println(Thread.currentThread().getName()+"没有抢到SimpleDateFormat");
            dateFormat =new SimpleDateFormat("yyyy-MM-dd");
            local.set(dateFormat);
        }
        return dateFormat;
    }
    public static void main(String[] args) {

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStrings = new String[]{"2018-08-01","2018-08-02","2018-08-03","2018-08-04","2018-08-05","2018-08-06","2018-08-07","2018-08-08"};
        MyThread[] myThreads = new MyThread[dateStrings.length];

        for(int i=0;i<dateStrings.length;i++){
            myThreads[i] = new MyThread(dateFormat,dateStrings[i]);
        }

        for(int i=0;i<dateStrings.length;i++){
            myThreads[i].start();
        }
    }
}

Mythread改成:

import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 *
 *  @Author: GuiRunning 郭贵荣
 *
 *  @Description:
 *
 *  @Date: 2018/6/30 11:08
 *
 */
public class MyThread extends Thread {

    private String dateString;

    MyThread(){
        super();
    }
    MyThread(SimpleDateFormat dateFormat,String dateString){
        super();
        this.dateString = dateString;
    }

    @Override
    public void run() {
        try{
            java.util.Date dateStr = FormatErrorTest.getSimpleDateFormat().parse(dateString);
            String newDateString = FormatErrorTest.getSimpleDateFormat().format(dateStr);
            if(!newDateString.equals(dateString)){
                System.out.println("时间转换出错了dateString="+dateString+"newDateString="+newDateString);
            }
            System.out.println(Thread.currentThread().getName()+"完成转换dateString="+dateString+"newDateString="+newDateString);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

运行结果:


image.png

相关文章

网友评论

      本文标题:SimpleDateFormat 线程安全的问题以及解决方案

      本文链接:https://www.haomeiwen.com/subject/jgwvfftx.html