溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

還在使用SimpleDateFormat?

發(fā)布時間:2020-08-18 00:22:39 來源:ITPUB博客 閱讀:173 作者:孤獨鍵客 欄目:編程語言

閱讀本文大概需要 3.2 分鐘。

前言

日常開發(fā)中,我們經常需要使用時間相關類,想必大家對 SimpleDateFormat 并不陌生。主要是用它進行時間的 格式化輸出和解析 ,挺方便快捷的,但是 SimpleDateFormat并不是一個線程安全的類 。在多線程情況下,會出現異常,想必有經驗的小伙伴也遇到過。

下面我們就來分析分析SimpleDateFormat為什么不安全?是怎么引發(fā)的?以及多線程下有那些SimpleDateFormat的解決方案?

先看看 《阿里巴巴開發(fā)手冊》 對于SimpleDateFormat是怎么看待的

還在使用SimpleDateFormat?cdn.xitu.io/2019/3/21/1699de9099afa9f4?w=728&h=328&f=jpeg&s=38443">

問題復現

一般我們在使用SimpleDateFormat的時候會把它定義為一個靜態(tài)變量,避免頻繁創(chuàng)建它們的對象實例,代碼如下:

還在使用SimpleDateFormat?

打印一下結果:

還在使用SimpleDateFormat?

是不是感覺沒什么毛???相信大多數人都是這樣使用的,也包括我。在單線程下自然沒毛病了,但是運用到多線程下就有大問題了。

測試下:

還在使用SimpleDateFormat?

控制臺打印結果:

還在使用SimpleDateFormat?

你看結果,發(fā)現了什么?直接崩了,部分線程獲取的時間不對,部分線程報 java.lang.NumberFormatException:multiple points 錯,線程直接掛死了。還有部分線程報 empty String 錯,值有問題。

多線程不安全原因

因為我們把SimpleDateFormat定義為 靜態(tài)變量 ,那么多線程下SimpleDateFormat的實例就會 被多個線程共享 ,B線程會讀取到A線程的時間,就會出現時間差異和其它各種問題。SimpleDateFormat和它繼承的DateFormat類也不是線程安全的。

來看看 SimpleDateFormat format() 方法的源碼:

還在使用SimpleDateFormat?

注意, calendar.setTime(date),SimpleDateFormat的format方法實際操作的就是 Calendar 。

因為我們聲明SimpleDateFormat為static變量,那么它的Calendar變量也就是一個共享變量, 可以被多個線程訪問 。

假設線程A執(zhí)行完calendar.setTime(date),把時間設置成2019-01-02,這時候被掛起,線程B獲得CPU執(zhí)行權。線程B也執(zhí)行到了calendar.setTime(date),把時間設置為2019-01-03。線程掛起,線程A繼續(xù)走,calendar還會被繼續(xù)使用(subFormat方法),而這時calendar用的是線程B設置的值了,而這就是引發(fā)問題的根源,出現時間不對,線程掛死等等。

其實SimpleDateFormat源碼上作者也給過我們提示:

還在使用SimpleDateFormat?

翻譯過來的意思就是:

日期格式未同步。

建議為每個線程創(chuàng)建單獨的格式實例。
如果多個線程同時訪問格式,則必須在外部同步

解決方案

只在需要的時候創(chuàng)建新實例,不用static修飾 。

還在使用SimpleDateFormat?

如上代碼,僅在需要用到的地方創(chuàng)建一個新的實例,就沒有線程安全問題,不過也加重了創(chuàng)建對象的負擔, 會頻繁地創(chuàng)建和銷毀對象,效率較低

采用Synchronized方式

還在使用SimpleDateFormat?

簡單粗暴,synchronized往上一套也可以解決線程安全問題,缺點自然就是 并發(fā)量大的時候會對性能有影響,線程阻塞

ThreadLocal

還在使用SimpleDateFormat?

ThreadLocal可以確保每個線程都可以得到單獨的一個SimpleDateFormat的對象,那么自然也就不存在競爭問題了。

基于JDK1.8的DateTimeFormatter

也是《阿里巴巴開發(fā)手冊》給我們的解決方案,對之前的代碼進行改造:

還在使用SimpleDateFormat?

運行結果就不貼了,不會出現報錯和時間不準確的問題。

DateTimeFormatter源碼上作者也加注釋說明了, 他的類是不可變的,并且是線程安全的。

還在使用SimpleDateFormat?

OK,現在是不是可以對你項目里的日期工具類進行一波優(yōu)化了呢?

知識擴展

在上述代碼中,我們通過創(chuàng)建一個線程池,來實現多線程循環(huán)打印日期的操作,但是我們創(chuàng)建方式你有沒有留意。

ExecutorService executorService = Executors.newFixedThreadPool(100);

當你IDEA安裝了阿里巴巴的代碼規(guī)范檢查插件時,使用Executors來創(chuàng)建線程池的話,會出現提示讓你手動創(chuàng)建線程池。

還在使用SimpleDateFormat?

因此,我們可以將創(chuàng)建線程池的代碼改成:

ExecutorService executorService = new ThreadPoolExecutor(100, 100,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

但是又會有提示,建議要為線程池中的線程設置名稱:

還在使用SimpleDateFormat?

改造之后的代碼為:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build(); ExecutorService executorService = new ThreadPoolExecutor(100, 100,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

這里會有個問題, ThreadFactoryBuilder() 在JDK1.8及之后被去除了,所以如果你的JDK低于1.8即可使用該方法,等于或高于1.8可采取其他方式設置線程名稱,也可用其他方式手動創(chuàng)建線程池。

為什么要這樣做

我們參考阿里巴巴的Java開發(fā)手冊內容:

關于Executors

還在使用SimpleDateFormat?

關于線程名稱

還在使用SimpleDateFormat?

再次簡單進一步解讀下:

  • newFixedThreadPool和newSingleThreadExecutor 由于最后一個參數即工作隊列是

還在使用SimpleDateFormat?

鏈表類型的阻塞隊列,而我們看其構造函數發(fā)現,默認隊列大小是整數的最大值?。?!

還在使用SimpleDateFormat?

所以如果請求太多,隊列很可能就耗費內存非常大導致OOM。

但是他們的線程數是固定的,而且一般不會太大,所以不會因為創(chuàng)建過多線程而導致OOM。

  • 再來看下newCachedThreadPool和newScheduledThreadPool

還在使用SimpleDateFormat?

其中第最大線程池大小是整數的最大值,因此線程可能不斷創(chuàng)建,乃至到整數的最大值個線程,很容易導致OOM。其中工作隊列使用的是 SynchronousQueue<E>,源碼頭部的注釋中有說明(截取的部分)。

A {@linkplain BlockingQueue blocking queue} in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa.

該類型的阻塞隊列每一個插入操作必須等待對應的元素被另一個線程所移除,反之亦然。

因此阻塞隊列不會無限拓展而導致OOM。

當我們學習和理解一些原則的同時,多注重源碼分析!??!



·END·

程序員的成長之路

路雖遠,行則必至

本文原發(fā)于 同名微信公眾號「程序員的成長之路」,回復「1024」你懂得,給個贊唄。

微信ID:cxydczzl


往期精彩回顧

程序員接私活的7大平臺利器

教你一招用 IDE 編程提升效率的騷操作!

大學期間的副業(yè)賺錢之道

一個對話讓你明白架構師是做什么的?

作為程序員的你,一年看幾本技術相關的書

5個相見恨晚的Linux命令

為啥程序員下班后只關顯示器從不關電腦?

送給程序員們的經典電子書大禮包

面試時如何優(yōu)雅地自我介紹?

支撐百萬并發(fā)的數據庫架構如何設計?


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI