SimpleDataFormat线程安全测试
介绍
在文章中看见SimpleDataFormat不是一个线程安全的程序,所以自己想在自己空闲的时候测试一些,然后去查看一下原因(很基础,分享一下)
过程
代码运行错误重现
当把SimpleDataFormat实例化的对象共享出来,给多个线程访问会出现A进程设置好了时间,然后挂起;B进程过来设置时间,A恢复了,读取时间混乱的问题。
代码测试:
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);
}
}
显示结果:(出现了错误的时间)
解决办法汇总
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,这样回收也会很快速