SimpleDataFormat多线程不安全问题

SimpleDataFormat线程安全测试

介绍

  在文章中看见SimpleDataFormat不是一个线程安全的程序,所以自己想在自己空闲的时候测试一些,然后去查看一下原因(很基础,分享一下)

过程

代码运行错误重现

  当把SimpleDataFormat实例化的对象共享出来,给多个线程访问会出现A进程设置好了时间,然后挂起;B进程过来设置时间,A恢复了,读取时间混乱的问题。

SimpleDat 线程A 线程B 1.实例化 2.设置时间(挂起等待资源) 3.设置时间(挂起) 4.继续执行,读取了B的是时间 SimpleDat 线程A 线程B

  代码测试:

public class SimpleDataFormatTest {
    private SimpleDateFormat simpleDataFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public String format(Date date){
        return simpleDataFormat.format(date);
    }

    public Date prase(String str) throws ParseException {
        return simpleDataFormat.parse(str);
    }

}

class demo{
    public static void main(String[] args) throws InterruptedException {
        SimpleDataFormatTest sdf=new SimpleDataFormatTest();
        ExecutorService executorService= Executors.newFixedThreadPool(100);
        for(int i=0;i<20;i++){
            executorService.execute(
                    ()->{
                        for(int j=0;j<10;j++) {
                            try {
                                System.out.println(sdf.prase("2019-01-11 12:11:11"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            );
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
    }
}

  显示结果:(出现了错误的时间)
f4e73eaebb982f567b57e03e8ab3d38e.png

解决办法汇总

1.新建对象

  每次不共享对象,都是新创建一个SimpleDataFormat的对象
  代码部分节选

for(int j=0;j<10;j++) {
	try {
		System.out.println("TimeParse:"+new SimpleDataFormatTest().prase("2000-01-01"));
	} catch (ParseException e) {
		e.printStackTrace();
}
2.添加synchronized
public synchronized Date parse(String strDate) throws ParseException {
        return sdf.parse(strDate);
}
3.ThreadLocal

  ThreadLocal可以保证每一个SimpleDateFormat都获取一个新的实例,所以不会存在竞争问题

public class TreadLocalTest {
    ThreadLocal<DateFormat> dateFormatThreadLocal=new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    public  Date parse(String dateStr) throws ParseException {
        return dateFormatThreadLocal.get().parse(dateStr);
    }

    public String format(Date date) {
        return dateFormatThreadLocal.get().format(date);
    }
}


class demo2{
    public static void main(String[] args){
        TreadLocalTest threadLocalTest=new TreadLocalTest();
        ExecutorService executors=Executors.newFixedThreadPool(100);
        for(int i=0;i<20;i++){
            executors.execute(
                    ()->{
                        for(int j=0;j<10;j++){
                            try {
                                System.out.println(threadLocalTest.parse("2019-01-11 12:11:11"));
                            } catch (ParseException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            );
        }
        executors.shutdown();
        try {
            executors.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.JDK1.8的DateTimeFormatter

  这个不存在资源占用问题,jdk1.8后可以使用这个

遗留问题

1)没有挖掘到为啥不是线程安全的?只是看见了相关的解释,共享同一个时间,资源占用,没有处理
2)ThreadLocal具体的操作逻辑
引用其他博客的回答
主要逻辑就是ThreadLocal搞了一个ThreadLocalMap用来存相关的值,每一次set的时候就是set这个map,获取的时候就是get这个map;
一个线程有一个map,这样回收也会很快速

猜你喜欢

转载自blog.csdn.net/zrqsyh/article/details/89251317
今日推荐