求求你別用SimpleDateFormat了!

前言git

 

啊哈哈,標題寫的比較隨意了,其實呢最近在各類面試以及博客中,SimpleDateFormat出鏡率確實是比較高了,爲何?其實聰明的大家確定知道,那必須是有坑唄,是的,那咱們就以案例來分析一下到底會有那些坑,或者還有沒有其餘更優的替代方案呢?github

 

正文面試

 

首先咱們來看一下可能會出如今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() 方法的源碼一探究竟。spa

 

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進行操做的時候,後面的線程有可能會覆蓋上一個線程設置好的值,此時就致使前面線程執行的結果被覆蓋而返回了一個錯誤的值。線程

 

那咱們該如何避免這個坑呢?code

 

一、使用ThreadLocal,每一個線程中返回各自的實例,避免了多線程環境中共用同一個實例而致使的問題。orm

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的使用過程當中也是有小坑須要注意的,你們能夠參考一下其餘的資料,之後能夠抽空聊聊這個話題。

 

二、推薦升級到JDK8+,使用LocalDateTime,LocalDate,LocalTime來代替,具體的用法請自行參考API,固然JDK8所帶來的Lambda,Stream等特性也是值得一試的。blog

 

三、使用三方包,推薦Joda-Time,對於日期的增減操做也是至關便捷。

 

github:https://github.com/JodaOrg/joda-time

相關文章
相關標籤/搜索