亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

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

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


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

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

                1.1 錯(cuò)誤示例

                錯(cuò)誤使用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,否則報(bào)錯(cuò) NumberFormatException * * private static final SimpleDateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); */ for (int i = 0; i { try { // 錯(cuò)誤寫法會(huì)導(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(); } }}

                報(bào)錯(cuò):

                1.2 非線程安全原因分析

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

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

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

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

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

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

                正常情況下:

                非線程安全的流程:

                1.3 解決方法

                方法1:每個(gè)線程都new一個(gè)SimpleDateFormat

                import java.text.SimpleDateFormat;public class SimpleDateFormatTest { public static void main(String[] args) { for (int i = 0; i { try { // 每個(gè)線程都new一個(gè) 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 為每個(gè)線程創(chuàng)建一個(gè)獨(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 錯(cuò)誤示例

                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 { // 時(shí)間格式化對(duì)象 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); // 得到時(shí)間對(duì)象 formatAndPrint(date); // 執(zhí)行時(shí)間格式化 } }); } threadPool.shutdown(); // 線程池執(zhí)行完任務(wù)之后關(guān)閉 } /** * 格式化并打印時(shí)間 */ private static void formatAndPrint(Date date) { String result = simpleDateFormat.format(date); // 執(zhí)行格式化 System.out.println("時(shí)間:" + result); // 打印最終結(jié)果 }}

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

                2.2 非線程安全原因分析

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

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

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

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

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

                2.3 解決方法

                同樣有三種解決方法

                方法1:每個(gè)線程都new一個(gè)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() { // 得到時(shí)間對(duì)象 Date date = new Date(finalI * 1000); // 執(zhí)行時(shí)間格式化 formatAndPrint(date); } }); } // 線程池執(zhí)行完任務(wù)之后關(guān)閉 threadPool.shutdown(); } /** * 格式化并打印時(shí)間 */ private static void formatAndPrint(Date date) { String result = new SimpleDateFormat("mm:ss").format(date); // 執(zhí)行格式化 System.out.println("時(shí)間:" + result); // 打印最終結(jié)果 }}

                方式2:synchronized等方式加鎖

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

                public class SimpleDateFormatTest { // 時(shí)間格式化對(duì)象 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); // 得到時(shí)間對(duì)象 formatAndPrint(date); // 執(zhí)行時(shí)間格式化 } }); } // 線程池執(zhí)行完任務(wù)之后關(guān)閉 threadPool.shutdown(); } /** * 格式化并打印時(shí)間 */ private static void formatAndPrint(Date date) { // 執(zhí)行格式化 String result = null; // 加鎖 synchronized (SimpleDateFormatTest.class) { result = simpleDateFormat.format(date); } // 打印最終結(jié)果 System.out.println("時(shí)間:" + result); }}

                方式3:使用ThreadLocal 為每個(gè)線程創(chuàng)建一個(gè)獨(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); // 得到時(shí)間對(duì)象 formatAndPrint(date); // 執(zhí)行時(shí)間格式化 }); } threadPool.shutdown(); // 線程池執(zhí)行完任務(wù)之后關(guān)閉 } /** * 格式化并打印時(shí)間 */ private static void formatAndPrint(Date date) { String result = dateFormatThreadLocal.get().format(date); // 執(zhí)行格式化 System.out.println(“時(shí)間:” + result); // 打印最終結(jié)果 }}

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

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

                相關(guān)推薦

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                  最近有關(guān)國內(nèi)各地的疫情大家也都有在持續(xù)關(guān)注,目前國內(nèi)各地疫情隔離時(shí)間也根據(jù)二十條防控措施有了新的調(diào)整。那么,5+3疫情防控從哪天開始算?對(duì)于密接的5+3隔離時(shí)間計(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
                工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息