-
1 # 讀樂樂傳送門
-
2 # 都市心聲
為什麼使用Java的SimpleDateFormat偶爾出現值不正確的情況?
雖然沒有具體描述在什麼情況下會偶爾出現值不正確的情況,不過我可以預測一下應該是在多執行緒的情況下出現的,那麼為什麼說在多執行緒的情況下會出現呢,對此,我們可以檢視一下SimpleDateFormat的原始碼,發現作者有一段註釋如下:
我們可以看到原來SimpleDateFormat並不是執行緒安全的,作者推薦為每一個執行緒建立一個單獨的例項,或者為SimpleDateFormat加鎖。
其實到了JDK1.8後,我們完全可以不用SimpleDateFormat了,可以用它新加入的類
java.time.format.DateTimeFormatter
,完美地解決了SimpleDateFormat的多執行緒安全問題,它使用instant代替Date,LocalDateTime替代Calendar,不過得改動老程式碼了,對不想改動的程式設計師來說有點難受,如果還是想繼續用SimpleDateFormat的話,那我們就把它用ThreadLocal包裝一下,如下所示:有類似問題的朋友可以動手操作一下,建議還是用DateTimeFormatter,因為包裝之後的代價也還是蠻大的。
-
3 # 程式猿W
我將從以下幾點進行說明:
1、SimpleDateFormat的使用
2、為什麼SimpleDateFormat執行緒不安全呢?
3、怎樣解決SimpleDateFormat的執行緒不安全物件
4、總結
SimpleDateFormat的使用我們通常都會寫一個日期處理工具類DateUtils,使用時直接使用這個例項來進行操作。程式碼如下:
那麼怎麼使用呢?
DateUtils.parseString("2020-05-01 10:02:02")
上述程式碼的呼叫,在大部分的時間裡都會工作的很好,但是當你的專案併發比較高的時候,問題就出來了,比如轉化的時間不正確,比如報錯,執行緒掛死。我們看下下面案例:
執行輸入如下:
報java.lang.NumberFormatException: multiple points錯誤,直接掛死,沒起來;
還有下面問題:我們只是解析 2020-05-01 10:02:02,下面輸出結果卻是各種各樣的結果。
為什麼SimpleDateFormat執行緒不安全呢?我們先按下JDK中是怎樣介紹SimpleDateFormat類的。
Date formats are not synchronized.
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized
Date formats 是執行緒不安全的。推薦為每個執行緒建立單獨的format例項。如果多執行緒併發訪問同一個format例項,必須加同步操作。
那下面我們分析原始碼來說明為什麼執行緒不安全?
因為我們在工具類中把SimpleDateFormat定義為靜態變數,那麼在多執行緒環境下SimpleDateFormat就會被多執行緒共享,B執行緒會讀取到A執行緒的時間,就會出現時間差異和其他問題。
那我們來看parse做了什麼?
從上面程式碼看(3)(4)(5) 操作不是原子性,當多個執行緒呼叫parse方法適合,比如A執行了(3)(4),也就是設定了cal物件,在執行程式碼(5) 前執行緒B 執行了程式碼(3) 清空了cal物件,由於多個執行緒使用的是一個cal物件,所以執行緒A執行(5) 的時候返回的是被執行緒B清空後的物件
怎樣解決SimpleDateFormat的執行緒不安全物件(1) 每次使用時new一個SimpleDateFormat 的 例項,這樣可以保證每個例項使用自己的Calendar例項,但是每次使用都需要new一個物件,並且使用後由於沒有其他引用,又需要回收,開銷會很大。
(2) 可以使用synchronized 對SimpleDtaFormat例項進行同步
(3) 使用ThreadLocal,這樣每個執行緒只需要使用一個SimpleDateFormate例項,這相比第一種方式 節省了物件的建立銷燬開銷,並且不需要使多個執行緒同步。
(4) 使用JDK8中的 DateTimeFormatter
上面說明此類是執行緒安全的。
總結SimpleDateFormat 是執行緒不安全的類,一般不要定義為 static 變數,如果定義為 static ,
必須加鎖,或者使用 DateUtils 工具類。
正例:注意執行緒安全,使用 DateUtils。亦推薦如下處理:
說明:如果是 JDK8 的應用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,
DateTimeFormatter 代替 SimpleDateFormat,官方給出的解釋:simple beautiful strong immutable thread-safe。
回覆列表
Java 文件有關 SimpleDateFormat 的描述:
“日期格式是非同步的。
建議為每個執行緒建立單獨的日期格式化例項。
如果多個執行緒併發訪問某個格式化例項,則必須保證外部呼叫同步性。“
正如文件中提到的那樣,可以為每個執行緒設定不同例項來解決這個問題。如果要共享例項,該如何實現?
1. ThreadLocal
可以使用 ThreadLocal 解決。Threadlocal 的 get() 方法會給當前執行緒提供正確的值。
2.JDK 8 新API
Java8 引入了新的日期時間 API,SimpleDateFormat 有了更好的替代者。如果繼續堅持使用 SimpleDateFormat 可以配合 ThreadLocal 一起使用。但既然已經有了更好的選擇,還是考慮用新的 API。
Java 8 提供了幾個執行緒安全的日期類,Java 文件中這麼描述:
“這個類是具有不可變和執行緒安全的特點。”
非常值得學習這些類的用法,包括 DateTimeFormatter、OffsetDateTime、ZonedDateTime、LocalDateTime、LocalDate 和 LocalTime。