SimpleDateFormat线程不安全


1,问题引发

 1 @Test  2 public void testParse() {  3     ExecutorService executorService = Executors.newCachedThreadPool();  4     List<String> dateStrList = Lists.newArrayList(  5             "2018-04-01 10:00:01",  6             "2018-04-02 11:00:02",  7             "2018-04-03 12:00:03",  8             "2018-04-04 13:00:04",  9             "2018-04-05 14:00:05"
10  ); 11     SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 12     for (String str : dateStrList) { 13         executorService.execute(() -> { 14             try { 15  simpleDateFormat.parse(str); 16                 TimeUnit.SECONDS.sleep(1); 17             } catch (Exception e) { 18  e.printStackTrace(); 19  } 20  }); 21  } 22 }

报错

2,原因

  在SimpleDateFormat转换日期是通过Calendar对象来操作的,SimpleDateFormat继承DateFormat类,DateFormat类中维护一个Calendar对象。

 

  通过DateFormat类中的注释可知:此处Calendar实例被用来进行日期-时间计算,既被用于format方法也被用于parse方法!

  在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,如下:

  可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题。

3,解决方法

  每一个使用SimpleDateFormat对象进行日期-时间进行format和parse方法的时候就创建一个新的SimpleDateFormat对象,用完就销毁即可!

 1 /**
 2  * 模拟并发环境下使用SimpleDateFormat的parse方法将字符串转换成Date对象  3  */
 4 @Test  5 public void testParseThreadSafe() {  6     ExecutorService executorService = Executors.newCachedThreadPool();  7     List<String> dateStrList = Lists.newArrayList(  8             "2018-04-01 10:00:01",  9             "2018-04-02 11:00:02", 10             "2018-04-03 12:00:03", 11             "2018-04-04 13:00:04", 12             "2018-04-05 14:00:05"
13  ); 14     for (String str : dateStrList) { 15         executorService.execute(() -> { 16             try { 17                 //创建新的SimpleDateFormat对象用于日期-时间的计算
18                 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 19  simpleDateFormat.parse(str); 20                 TimeUnit.SECONDS.sleep(1); 21                 simpleDateFormat = null; //销毁对象
22             } catch (Exception e) { 23  e.printStackTrace(); 24  } 25  }); 26  } 27 }

  使用SimpleDateFormat对象进行日期-时间计算时,如果SimpleDateFormat是多个线程共享的就会有线程安全问题!应该让每一个线程都有一个独立的SimpleDateFormat对象用于日期-时间的计算!此时就可以使用ThreadLocal将SimpleDateFormat绑定到线程上,是的该线程上的日期-时间计算顺序的使用SimpleDateFormat对象,这样也可以避免线程安全问题。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM