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