多線程避免使用SimpleDateFormat及替代方案

先來看一個多線程下使用例子,看到運行結果會出現異常:java

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class SimpleDateFormateTest {
 
    public static void main(String[] args) {
        final DateFormat df = new SimpleDateFormat("yyyyMMdd,HHmmss");
        ExecutorService ts = Executors.newFixedThreadPool(100);
        for (;;) {
            ts.execute(new Runnable() {         
                @Override
                public void run() {
                    try {
                      //生成隨機數,格式化日期
                      String format =  df.format(new Date(Math.abs(new Random().nextLong())));
                      System.out.println(format);
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                }
            });
        }
    }    
}

 

運行結果:nginx

 

 

 

在併發環境下使用SimpleDateFormat,正常的打開放式以下:安全

爲了可以在多線程環境下使用SimpleDateFormat,有這六種方法:多線程

方法一

在須要執行格式化的地方都新建SimpleDateFormat實例,使用局部變量來存放SimpleDateFormat實例併發

public static String formatDate(Date date)throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); }

這種方法可能會致使短時間內建立大量的SimpleDateFormat實例,如解析一個excel表格裏的字符串日期。dom

方法二

爲了不建立大量的SimpleDateFormat實例,每每會考慮把SimpleDateFormat實例設爲靜態成員變量,共享SimpleDateFormat對象。這種狀況下就得對SimpleDateFormat添加同步。ide

private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException{ synchronized(sdf){ return sdf.format(date); } }

這種方法的缺點也很明顯,就是在高併發的環境下會致使解析被阻塞。高併發

方法三 

方法加同步鎖synchronized,在同一時刻,只有一個線程能夠執行類中的某個方法。post

缺點:性能較差,每次都要等待鎖釋放後其餘線程才能進入。性能

方案四 使用第三方包

這個我有嘗試cn.hutoolcommon-lang3提供的FastDateFormat 
最後的結果其實並不滿意,由於這兩個包都沒能幫助我檢查非正常時間,好比2018-07-32這種日期也被認爲是正確的時期格式了

 

方法五(推薦

要在高併發環境下能有比較好的體驗,能夠使用ThreadLocal來限制SimpleDateFormat只能在線程內共享,這樣就避免了多線程致使的線程安全問題。

private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static String format(Date date) { return threadLocal.get().format(date); }

方案六 DateTimeFormatter使用

Java8提供了新的日期時間API,其中包括用於日期時間格式化的DateTimeFormatter,它與SimpleDateFormat的有什麼區別呢?

問題解決

二者最大的區別是,Java8的DateTimeFormatter也是線程安全的,而SimpleDateFormat並非線程安全。

解析日期

String dateStr= "2016年10月25日"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); LocalDate date= LocalDate.parse(dateStr, formatter); 

日期轉換爲字符串

LocalDateTime now = LocalDateTime.now(); DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm a"); String nowStr = now .format(format);

由DateTimeFormatter的靜態方法ofPattern()構建日期格式,LocalDateTime和LocalDate等一些表示日期或時間的類使用parse和format方法把日期和字符串作轉換。

使用新的API,整個轉換過程都不須要考慮線程安全的問題。

相關文章
相關標籤/搜索