前言
啊哈哈,標題寫的比較隨意了,其實呢最近在各種面試以及博客中,SimpleDateFormat出鏡率確實是比較高了,為什么?其實聰明的你們肯定知道,那必須是有坑唄,是的,那我們就以案例來分析一下到底會有那些坑,或者還有沒有其他更優的替代方案呢?
正文
首先我們來看一下可能會出現在DateUtils中的寫法:
private static final SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static Date formatDate(String date) throws ParseException { return dayFormat.parse(date); }
當我們在單線程的程序中調用 formatDate(date) ,此時並不會出現任何問題(如果這也出問題那還玩什么...) ,然而當我們的程序在多線程並發執行調用這個方法的時候。
ExecuterService es = ExecuterService.newFixedThreadPool(50); for( ... ){ es.execute( () -> { System.out.println(parse("2018-11-11 10:35:20")); }) }
此時你會發現打印出來的時間有些是錯誤,程序甚至會拋出異常NumberFormatException??為什么會出現這種情況呢?我們可以直接查看SimpleDateFormat.parse() 方法的源碼一探究竟。
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern\[i\] >>> 8; int count = compiledPattern\[i++\] & 0xff; if (count == 255) { count = compiledPattern\[i++\] << 16; count |= compiledPattern\[i++\]; }
從源碼可以看到,在多線程的環境中,執行到第五行 calendar進行操作的時候,后面的線程有可能會覆蓋上一個線程設置好的值,此時就導致前面線程執行的結果被覆蓋而返回了一個錯誤的值。
那我們該如何避免這個坑呢?
1、使用ThreadLocal,每個線程中返回各自的實例,避免了多線程環境中共用同一個實例而導致的問題。
private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>(); public static Date formatDate(String date) throws ParseException { SimpleDateFormat dayFormat = getSimpleDateFormat(); return dayFormat.parse(date); } private static SimpleDateFormat getSimpleDateFormat() { SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get(); if (simpleDateFormat == null){ simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"); simpleDateFormatThreadLocal.set(simpleDateFormat); } return simpleDateFormat; }
需要注意一點的是,ThreadLocal的使用過程中也是有小坑需要注意的,大家可以參考一下其他的資料,以后可以抽空聊聊這個話題。
2、推薦升級到JDK8+,使用LocalDateTime,LocalDate,LocalTime來代替,具體的用法請自行參考API,當然JDK8所帶來的Lambda,Stream等特性也是值得一試的。
3、使用三方包,推薦Joda-Time,對於日期的增減操作也是相當便捷。
github:https://github.com/JodaOrg/joda-time