在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    還在用 SimpleDateFormat 做時間格式化?小心項目崩掉

    還在用 SimpleDateFormat 做時間格式化?小心項目崩掉


    SimpleDateFormat在多線程環(huán)境下存在線程安全問題。

    1 SimpleDateFormat.parse() 方法的線程安全問題

    1.1 錯誤示例

    錯誤使用SimpleDateFormat.parse()的代碼如下:

    import java.text.SimpleDateFormat;public class SimpleDateFormatTest { private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); public static void main(String[] args) { /** * SimpleDateFormat線程不安全,沒有保證線程安全(沒有加鎖)的情況下,禁止使用全局SimpleDateFormat,否則報錯 NumberFormatException * * private static final SimpleDateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); */ for (int i = 0; i { try { // 錯誤寫法會導致線程安全問題 System.out.println(Thread.currentThread().getName() + “–” + SIMPLE_DATE_FORMAT.parse(“2020-06-01 11:35:00”)); } catch (Exception e) { e.printStackTrace(); } }, “Thread-” + i); thread.start(); } }}

    報錯:

    1.2 非線程安全原因分析

    查看源碼中可以看到:SimpleDateFormat繼承DateFormat類,SimpleDateFormat轉換日期是通過繼承自DateFormat類的Calendar對象來操作的,Calendar對象會被用來進行日期-時間計算,既被用于format方法也被用于parse方法。

    SimpleDateFormat 的 parse(String source) 方法 會調用繼承自父類的 DateFormat 的 parse(String source) 方法

    DateFormat 的 parse(String source) 方法會調用SimpleDateFormat中重寫的 parse(String text, ParsePosition pos) 方法,該方法中有個地方需要關注

    SimpleDateFormat 中重寫的 parse(String text, ParsePosition pos) 方法中調用了 establish(calendar) 這個方法:

    該方法中調用了 Calendar 的 clear() 方法

    可以發(fā)現整個過程中Calendar對象它并不是線程安全的,如果,a線程將calendar清空了,calendar 就沒有新值了,恰好此時b線程剛好進入到parse方法用到了calendar對象,那就會產生線程安全問題了!

    正常情況下:

    非線程安全的流程:

    1.3 解決方法

    方法1:每個線程都new一個SimpleDateFormat

    import java.text.SimpleDateFormat;public class SimpleDateFormatTest { public static void main(String[] args) { for (int i = 0; i { try { // 每個線程都new一個 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); System.out.println(Thread.currentThread().getName() + “–” + simpleDateFormat.parse(“2020-06-01 11:35:00”)); } catch (Exception e) { e.printStackTrace(); } }, “Thread-” + i); thread.start(); } }}

    方式2:synchronized等方式加鎖

    public class SimpleDateFormatTest { private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); public static void main(String[] args) { for (int i = 0; i { try { synchronized (SIMPLE_DATE_FORMAT) { System.out.println(Thread.currentThread().getName() + “–” + SIMPLE_DATE_FORMAT.parse(“2020-06-01 11:35:00”)); } } catch (Exception e) { e.printStackTrace(); } }, “Thread-” + i); thread.start(); } }}

    方式3:使用ThreadLocal 為每個線程創(chuàng)建一個獨立變量

    import java.text.DateFormat;import java.text.SimpleDateFormat;public class SimpleDateFormatTest { private static final ThreadLocal SAFE_SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”)); public static void main(String[] args) { for (int i = 0; i { try { System.out.println(Thread.currentThread().getName() + “–” + SAFE_SIMPLE_DATE_FORMAT.get().parse(“2020-06-01 11:35:00”)); } catch (Exception e) { e.printStackTrace(); } }, “Thread-” + i); thread.start(); } }}

    ThreadLocal的詳細使用細節(jié)見:

    https://blog.csdn.net/QiuHaoqian/article/details/117077792

    2 SimpleDateFormat.format() 方法的線程安全問題

    2.1 錯誤示例

    import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class SimpleDateFormatTest { // 時間格式化對象 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“mm:ss”); public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程池執(zhí)行任務 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000)); for (int i = 0; i < 1000; i++) { int finalI = i; // 執(zhí)行任務 threadPool.execute(new Runnable() { @Override public void run() { Date date = new Date(finalI * 1000); // 得到時間對象 formatAndPrint(date); // 執(zhí)行時間格式化 } }); } threadPool.shutdown(); // 線程池執(zhí)行完任務之后關閉 } /** * 格式化并打印時間 */ private static void formatAndPrint(Date date) { String result = simpleDateFormat.format(date); // 執(zhí)行格式化 System.out.println("時間:" + result); // 打印最終結果 }}

    從上述結果可以看出,程序的打印結果竟然是有重復內容的,正確的情況應該是沒有重復的時間才對。

    2.2 非線程安全原因分析

    為了找到問題所在,查看原因 SimpleDateFormat 中 format 方法的源碼來排查一下問題,format 源碼如下:

    從上述源碼可以看出,在執(zhí)行任務 SimpleDateFormat.format() 方法時,會使用 calendar.setTime() 方法將輸入的時間進行轉換,那么我們想象一下這樣的場景:

    • 線程 1 執(zhí)行了 calendar.setTime(date) 方法,將用戶輸入的時間轉換成了后面格式化時所需要的時間;
    • 線程 1 暫停執(zhí)行,線程 2 得到 CPU 時間片開始執(zhí)行;
    • 線程 2 執(zhí)行了 calendar.setTime(date) 方法,對時間進行了修改;
    • 線程 2 暫停執(zhí)行,線程 1 得出 CPU 時間的繼續(xù)執(zhí)行,因為線程 1 和線程 2 使用的是同一對象,而時間已經被線程 2 修改了,所以此時當前線程 1 繼續(xù)執(zhí)行的時候就會出現線程安全的問題了。

    正常的情況下,程序的執(zhí)行是這樣的:

    非線程安全的執(zhí)行流程是這樣的:

    2.3 解決方法

    同樣有三種解決方法

    方法1:每個線程都new一個SimpleDateFormat

    public class SimpleDateFormatTest { public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程池執(zhí)行任務 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000)); for (int i = 0; i < 1000; i++) { int finalI = i; // 執(zhí)行任務 threadPool.execute(new Runnable() { @Override public void run() { // 得到時間對象 Date date = new Date(finalI * 1000); // 執(zhí)行時間格式化 formatAndPrint(date); } }); } // 線程池執(zhí)行完任務之后關閉 threadPool.shutdown(); } /** * 格式化并打印時間 */ private static void formatAndPrint(Date date) { String result = new SimpleDateFormat("mm:ss").format(date); // 執(zhí)行格式化 System.out.println("時間:" + result); // 打印最終結果 }}

    方式2:synchronized等方式加鎖

    所有的線程必須排隊執(zhí)行某些業(yè)務才行,這樣無形中就降低了程序的運行效率了

    public class SimpleDateFormatTest { // 時間格式化對象 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“mm:ss”); public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程池執(zhí)行任務 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000)); for (int i = 0; i < 1000; i++) { int finalI = i; // 執(zhí)行任務 threadPool.execute(new Runnable() { @Override public void run() { Date date = new Date(finalI * 1000); // 得到時間對象 formatAndPrint(date); // 執(zhí)行時間格式化 } }); } // 線程池執(zhí)行完任務之后關閉 threadPool.shutdown(); } /** * 格式化并打印時間 */ private static void formatAndPrint(Date date) { // 執(zhí)行格式化 String result = null; // 加鎖 synchronized (SimpleDateFormatTest.class) { result = simpleDateFormat.format(date); } // 打印最終結果 System.out.println("時間:" + result); }}

    方式3:使用ThreadLocal 為每個線程創(chuàng)建一個獨立變量

    public class SimpleDateFormatTest { // 創(chuàng)建 ThreadLocal 并設置默認值 private static ThreadLocal dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(“mm:ss”)); public static void main(String[] args) { // 創(chuàng)建線程池執(zhí)行任務 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000)); // 執(zhí)行任務 for (int i = 0; i { Date date = new Date(finalI * 1000); // 得到時間對象 formatAndPrint(date); // 執(zhí)行時間格式化 }); } threadPool.shutdown(); // 線程池執(zhí)行完任務之后關閉 } /** * 格式化并打印時間 */ private static void formatAndPrint(Date date) { String result = dateFormatThreadLocal.get().format(date); // 執(zhí)行格式化 System.out.println(“時間:” + result); // 打印最終結果 }}

    文章來源:blog.csdn.net/QiuHaoqian/article/details/116594422

    鄭重聲明:本文內容及圖片均整理自互聯(lián)網,不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶投稿
    上一篇 2022年6月19日 18:09
    下一篇 2022年6月19日 18:09

    相關推薦

    • 分享4條發(fā)微商朋友圈的方法(微商朋友圈應該怎么發(fā))

      對于微商朋友來說,朋友圈的重要性不言而喻了。 那么微商的朋友圈到底該怎么發(fā)呢? 為什么同樣是經營一個朋友圈,有的微商看起來逼格滿滿,實際效果也不錯;而有的卻動都不動就被屏蔽甚至拉黑…

      2022年11月27日
    • 30個無加盟費的項目(茶顏悅色奶茶店加盟費多少)

      茶顏悅色又爆了,8月18日,茶顏悅色南京門店正式開業(yè),開張不到半小時,門店就人滿為患,消費者的購買熱情十分高漲,而由于人流量過大造成擁堵,茶顏悅色也不得不暫停營業(yè)。 當然,這里面排…

      2022年11月27日
    • 怎么轉行總結出成功轉行的3個步驟

      01 前段時間,由麥可思研究院發(fā)布的《就業(yè)藍皮書:2019年中國大學生就業(yè)報告》顯示,2018大學畢業(yè)生半年內的離職率為33%,主動離職的主要原因是“個人發(fā)展空間不夠”和“薪資福利…

      2022年11月26日
    • 凈利潤率越高越好嗎(凈利潤率多少合適)

      一、持續(xù)增收不增利,平均凈利潤率首次跌入個位數 2021年,增收不增利依舊是行業(yè)主流。具體來看,大部分企業(yè)營業(yè)收入呈增長態(tài)勢,E50企業(yè)平均同比增速達到17.3%,但是利潤增速則明…

      2022年11月26日
    • 《寶可夢朱紫》夢特性怎么獲得?隱藏特性獲取方法推薦

      寶可夢朱紫里有很多寶可夢都是擁有夢特性會變強的寶可夢,很多玩家不知道夢特性怎么獲得,下面就給大家?guī)韺毧蓧糁熳想[藏特性獲取方法推薦,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 …

      2022年11月25日
    • 《寶可夢朱紫》奇魯莉安怎么進化?奇魯莉安進化方法分享

      寶可夢朱紫中的奇魯莉安要怎么進化呢?很多玩家都不知道,下面就給大家?guī)韺毧蓧糁熳掀骠斃虬策M化方法分享,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 奇魯莉安進化方法分享 奇魯莉安…

      2022年11月25日
    • 中國疫情為何突然嚴重了(大連疫情為何突然增加那么多)

      有在關注國內此次疫情的小伙伴應該注意到了,近期國內各地的疫情情況也都是比較嚴峻的。而對于進入十一月份后各地突然爆發(fā)的疫情,大家也都第一時間查詢原因。中國疫情為何突然嚴重了?為什么國…

      2022年11月25日
    • 為什么北京疫情越來越嚴重(疫情為什么越控制越嚴重)

      有在關注北京疫情的小伙伴應該注意到了,這幾天北京疫情的新增數據情況也開始越發(fā)的嚴重起來,大家對北京此次疫情發(fā)展情況也都不斷分析。為什么北京疫情越來越嚴重?導致北京本輪疫情的原因是什…

      2022年11月25日
    • 5+3疫情防控從哪天開始算(遼寧疫情防控最新政策)

      最近有關國內各地的疫情大家也都有在持續(xù)關注,目前國內各地疫情隔離時間也根據二十條防控措施有了新的調整。那么,5+3疫情防控從哪天開始算?對于密接的5+3隔離時間計算大家還是比較關心…

      2022年11月25日
    • 藍碼怎么變綠碼需要幾天(藍碼怎么變綠碼需要幾天)

      大家都知道健康碼的顏色有紅碼、綠碼、黃碼,近日湖南健康碼上線“藍碼”,不少小伙伴發(fā)現自己健康碼變藍了,都想趕緊恢復綠碼,那么藍碼怎么變綠碼需要幾天?下面小編為大家?guī)硭{碼變綠碼需要…

      2022年11月25日

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息