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

关于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<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(DATE_FORMAT));

    // ThreadLocal的另外一种写法
    private static ThreadLocal<SimpleDateFormat> 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<SimpleDateFormat> threadLocal3 = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new SimpleDateFormat(DATE_FORMAT);
        }
    };

// 写法之二
    private static ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(DATE_FORMAT));

// 写法之三
    private static ThreadLocal<SimpleDateFormat> 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

嗯。好了。先记一些吧。

posted @ 2023-02-21 18:39  aaacarrot  阅读(100)  评论(0编辑  收藏  举报