求求你別用SimpleDateFormat了!


前言

 

啊哈哈,標題寫的比較隨意了,其實呢最近在各種面試以及博客中,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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM