高并发之——SimpleDateFormat类的线程安全问题和解决方案

作者: adm 分类: java 发布时间: 2023-12-30

关于SimpleDateFormat
熟悉Java的同学知道这个类是线程不安全的,但究竟是怎样不安全法,什么原因产生的线程不安全?估计未必全部人都能够答得上来(我也不能,emmmm)

呃,想更好地了解关于 SimpleDateFormat 这个工具类的线程不安全的原因,推荐一位大佬的博客,请参考:高并发之——SimpleDateFormat类的线程安全问题和解决方案

正文
1、SimpleDateFormat 线程不安全的原因
请参考上述博文

2、解决方案
解决方案是有很多的

把 SimpleDateFormat 放到方法里面(不太好)
加 synchronized(不太好)
加 lock (不太好)
使用threadLocal (推荐)
使用 DateTimeFormatter (推荐)
使用joda-time方式(需要引入新依赖,看情况决定使用)
3、列一些demo代码
是为自己记得,参考的博文解释得更清楚些

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
 
public class SimpleDateFormatFix02 {
 
    //执行总次数
    private static final int EXECUTE_COUNT = 1000;
    //同时运行的线程数量
    private static final int THREAD_COUNT = 20;
 
    private static final String DATE_FORMAT = "yyyy-MM-dd";
 
    private static ThreadLocal threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(DATE_FORMAT));
 
    // ThreadLocal的另外一种写法
    private static ThreadLocal threadLocal2 = new ThreadLocal();
    private static SimpleDateFormat getDataFormat(){
        SimpleDateFormat simpleDateFormat = threadLocal2.get();
        if(null == simpleDateFormat){
            simpleDateFormat = new SimpleDateFormat(DATE_FORMAT);
            threadLocal2.set(simpleDateFormat);
        }
        return simpleDateFormat;
    }
    // end
 
 
    public static void main(String[] args) throws InterruptedException {
        final Semaphore semaphore = new Semaphore(THREAD_COUNT);
        final CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
 
        ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000));
        for (int i = 0; i < EXECUTE_COUNT; i++) {
            executor.execute(() -> {
                try {
                    semaphore.acquire();
                    try {
                        threadLocal.get().parse("2022-01-01");
                    } catch (ParseException e) {
                        e.printStackTrace();
                        System.out.println("转换失败");
                        System.exit(1);
                    } catch (NumberFormatException e) {
                        e.printStackTrace();
                        System.out.println("转换失败");
                        System.exit(1);
                    }
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.exit(1);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executor.shutdown();
        System.out.println("finish success!");
 
    }
}
 

嗯,只记录一下ThreadLocal 的写法,其它写法就不写了。

再说多句,
关于 ThreadLocal 的写法也有几种写法,

// 写法之一
    private static ThreadLocal threadLocal3 = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new SimpleDateFormat(DATE_FORMAT);
        }
    };
 
// 写法之二
    private static ThreadLocal threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(DATE_FORMAT));
 
// 写法之三
    private static ThreadLocal threadLocal2 = new ThreadLocal();
    private static SimpleDateFormat getDataFormat(){
        SimpleDateFormat simpleDateFormat = threadLocal2.get();
        if(null == simpleDateFormat){
            simpleDateFormat = new SimpleDateFormat(DATE_FORMAT);
            threadLocal2.set(simpleDateFormat);
        }
        return simpleDateFormat;
    }
    // end

嗯。好了。先记一些吧。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!