前言
测试日期格式化类SimpleDateFormat
为线程不安全类,优化项目工具类,提升安全性能。
测试类
准备测试类DateFormatTest
,里面包括一个集成Thread
的Job
类,重写方法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
来说,它是线程安全类,推荐。
后记
除了以上几种方法,还有apache
的DateFormatUtils
,FastDateFormat
等封装,安全也高效。
最后:
- 当需要使用多线程时,有个变量恰巧不需要共享,此时就不必使用synchronized这么麻烦的关键字来锁住,每个线程都相当于在堆内存中开辟一个空间,线程中带有对共享变量的缓冲区,通过缓冲区将堆内存中的共享变量进行读取和操作,ThreadLocal相当于线程内的内存,一个局部变量。每次可以对线程自身的数据读取和操作,并不需要通过缓冲区与 主内存中的变量进行交互。并不会像synchronized那样修改主内存的数据,再将主内存的数据复制到线程内的工作内存。ThreadLocal可以让线程独占资源,存储于线程内部,避免线程堵塞造成CPU吞吐下降。
- 在每个Thread中包含一个ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的对象,value是独享数据。