免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

      還在用 SimpleDateFormat 做時間格式化?小心項(xiàng)目崩掉

      還在用 SimpleDateFormat 做時間格式化?小心項(xiàng)目崩掉


      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 { // 錯誤寫法會導(dǎo)致線程安全問題 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轉(zhuǎn)換日期是通過繼承自DateFormat類的Calendar對象來操作的,Calendar對象會被用來進(jìn)行日期-時間計(jì)算,既被用于format方法也被用于parse方法。

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

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

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

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

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

      正常情況下:

      非線程安全的流程:

      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)建一個獨(dú)立變量

      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的詳細(xì)使用細(xì)節(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í)行任務(wù) ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000)); for (int i = 0; i < 1000; i++) { int finalI = i; // 執(zhí)行任務(wù) threadPool.execute(new Runnable() { @Override public void run() { Date date = new Date(finalI * 1000); // 得到時間對象 formatAndPrint(date); // 執(zhí)行時間格式化 } }); } threadPool.shutdown(); // 線程池執(zhí)行完任務(wù)之后關(guān)閉 } /** * 格式化并打印時間 */ private static void formatAndPrint(Date date) { String result = simpleDateFormat.format(date); // 執(zhí)行格式化 System.out.println("時間:" + result); // 打印最終結(jié)果 }}

      從上述結(jié)果可以看出,程序的打印結(jié)果竟然是有重復(fù)內(nèi)容的,正確的情況應(yīng)該是沒有重復(fù)的時間才對。

      2.2 非線程安全原因分析

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

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

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

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

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

      2.3 解決方法

      同樣有三種解決方法

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

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

      方式2:synchronized等方式加鎖

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

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

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

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

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

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

      相關(guān)推薦

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

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

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

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

        2022年11月27日
      • 怎么轉(zhuǎn)行總結(jié)出成功轉(zhuǎn)行的3個步驟

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        2022年11月25日

      聯(lián)系我們

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