Simpledateformat 线程不安全解决方案

前言

测试日期格式化类SimpleDateFormat为线程不安全类,优化项目工具类,提升安全性能。

测试类

准备测试类DateFormatTest,里面包括一个集成ThreadJob类,重写方法run()进行时间格式化。
主程序建立线程加入具体任务,代码详情如下:

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

/**
 * 测试SimpleDateFormat为线程不安全类
 * @author terry
 * @since 11/10/2019
 */
public class DateFormatTest {
    static ThreadLocal<SimpleDateFormat> thread = new ThreadLocal(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMddHHmm");
        }
    };
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");

    static class Job extends Thread{
        private String dateStr;

        public Job(String dateStr) { this.dateStr = dateStr; }
        @Override
        public void run() {
            Date date;
            try {
                /*方法一:使用局部变量
                date = new SimpleDateFormat("yyyyMMddHHmm")parse(dateStr);
                */
                /*方法二:加锁加锁synchronized(lock)
                synchronized(sdf){
                    date = sdf.parse(dateStr);
                }*/
                /*方法三:ThreadLocal
                date = thread.get().parse(dateStr);
                */
                /*方法四:DateTimeFormatter
                LocalDate localDate = LocalDate.parse(dateStr,DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
                Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
                date = Date.from(instant);
                */
                //apache commons-lang包的DateFormatUtils或者FastDateFormat实现,apache保证是线程安全的,并且更高效
                date = sdf.parse(dateStr);
            } catch (Exception e){
                System.out.println("Error:"+e.getMessage());
            }
            System.out.println(dateStr+"--->"+date);
        }
    }
    public static void main(String[] args) throws Exception {
        String[] date = {"201910081415", "201910081416", "201910081417"};
        for (int i = 0; i < date.length; i++) {
            Thread thread = new Job(date[i]);
            thread.start();
        }
    }
}

下面开始测试,所有测试均在主程序try-catch中进行。

测试

date = sdf.parse(dateStr);

输出:

Error:multiple points
Error:multiple points
201910081417--->Sun Oct 08 14:17:00 CST 2220

共享SimpleDateFormat,线程不安全,多线程不能直接使用。

方法一 局部变量

date = new SimpleDateFormat("yyyyMMddHHmm")parse(dateStr);

输出:

201910081415--->Tue Oct 08 14:15:00 CST 2019
201910081416--->Tue Oct 08 14:16:00 CST 2019
201910081417--->Tue Oct 08 14:17:00 CST 2019

改为局部变量,其实就是每次使用时候new一个SimpleDateFormat对象,似乎浪费资源

方法二 同步加锁synchronized(lock)

synchronized(sdf){
      date = sdf.parse(dateStr);
}

输出:

201910081415--->Tue Oct 08 14:15:00 CST 2019
201910081416--->Tue Oct 08 14:16:00 CST 2019
201910081417--->Tue Oct 08 14:17:00 CST 2019

使用同步关键字synchronized锁住当前SimpleDateFormat,会堵塞,性能差。

方法三 ThreadLocal

static ThreadLocal<SimpleDateFormat> thread = new ThreadLocal(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMddHHmm");
        }
};

输出:

201910081417--->Tue Oct 08 14:17:00 CST 2019
201910081415--->Tue Oct 08 14:15:00 CST 2019
201910081416--->Tue Oct 08 14:16:00 CST 2019

ThreadLocal是"本地线程",只有当前线程服务,典型的以空间换时间,我感觉还可以接收。

方法四 DateTimeFormatter

LocalDate localDate = LocalDate.parse(dateStr,DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
date = Date.from(instant);

输出:

201910081415--->Tue Oct 08 00:00:00 CST 2019
201910081416--->Tue Oct 08 00:00:00 CST 2019
201910081417--->Tue Oct 08 00:00:00 CST 2019

DateTimeFormatter 是Java8的新特性,主要用来格式化日期以及时间,相对SimpleDateFormat来说,它是线程安全类,推荐。

后记

除了以上几种方法,还有apacheDateFormatUtils,FastDateFormat等封装,安全也高效。
最后:

  1. 当需要使用多线程时,有个变量恰巧不需要共享,此时就不必使用synchronized这么麻烦的关键字来锁住,每个线程都相当于在堆内存中开辟一个空间,线程中带有对共享变量的缓冲区,通过缓冲区将堆内存中的共享变量进行读取和操作,ThreadLocal相当于线程内的内存,一个局部变量。每次可以对线程自身的数据读取和操作,并不需要通过缓冲区与 主内存中的变量进行交互。并不会像synchronized那样修改主内存的数据,再将主内存的数据复制到线程内的工作内存。ThreadLocal可以让线程独占资源,存储于线程内部,避免线程堵塞造成CPU吞吐下降。
  2. 在每个Thread中包含一个ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的对象,value是独享数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值