pos機費改時間,還在用 SimpleDateFormat 做時間格式化

 新聞資訊  |   2023-05-17 10:10  |  投稿人:pos機之家

網上有很多關于pos機費改時間,還在用 SimpleDateFormat 做時間格式化的知識,也有很多人為大家解答關于pos機費改時間的問題,今天pos機之家(www.tonybus.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、pos機費改時間

pos機費改時間

SimpleDateFormat.parse() 方法的線程安全問題錯誤示例非線程安全原因分析解決方法SimpleDateFormat.format() 方法的線程安全問題錯誤示例非線程安全原因分析解決方法

SimpleDateFormat在多線程環境下存在線程安全問題。

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 < 20; ++i) { Thread thread = new Thread(() -> { 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() 方法

可以發現整個過程中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 < 20; ++i) { Thread thread = new Thread(() -> { 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 < 20; ++i) { Thread thread = new Thread(() -> { 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 為每個線程創建一個獨立變量

import java.text.DateFormat;import java.text.SimpleDateFormat;public class SimpleDateFormatTest { private static final ThreadLocal<DateFormat> 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 < 20; ++i) { Thread thread = new Thread(() -> { 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的詳細使用細節見:

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

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

2.2 非線程安全原因分析

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

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

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

正常的情況下,程序的執行是這樣的:

非線程安全的執行流程是這樣的:

2.3 解決方法

同樣有三種解決方法

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

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

方式2:synchronized等方式加鎖

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

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

方式3:使用ThreadLocal 為每個線程創建一個獨立變量

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

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

以上就是關于pos機費改時間,還在用 SimpleDateFormat 做時間格式化的知識,后面我們會繼續為大家整理關于pos機費改時間的知識,希望能夠幫助到大家!

轉發請帶上網址:http://www.tonybus.com/news/44459.html

你可能會喜歡:

版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 babsan@163.com 舉報,一經查實,本站將立刻刪除。