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 }
报错安全
在SimpleDateFormat转换日期是经过Calendar对象来操做的,SimpleDateFormat继承DateFormat类,DateFormat类中维护一个Calendar对象。并发
经过DateFormat类中的注释可知:此处Calendar实例被用来进行日期-时间计算,既被用于format方法也被用于parse方法!ui
在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,以下:spa
可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,若是此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题。线程
每个使用SimpleDateFormat对象进行日期-时间进行format和parse方法的时候就建立一个新的SimpleDateFormat对象,用完就销毁便可!3d
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对象,这样也能够避免线程安全问题。code