SimpleDateFormat線程不安全原因

SimpleDateFormat線上報了各種各樣奇怪的格式轉換的錯誤,通過小的demo來解決這個問題

public static void main(String[] args) {
	final DateFormat YYYY_MM_DD_HH_MM_SS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	Callable<Date> task = new Callable<Date>() {
		public Date call() throws Exception {
			return YYYY_MM_DD_HH_MM_SS.parse("2016-12-18 15:00:34");
		}
	};
	
	// 創建5個線程的線程池
	ExecutorService exec = Executors.newFixedThreadPool(5);
	List<Future<Date>> results = new ArrayList<Future<Date>>();
	for (int i = 0; i < 10; i++) {
		results.add(exec.submit(task));
	}
	exec.shutdown();
	// 輸出結果
	for (Future<Date> result : results) {
		try {
			System.out.println(result.get());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

錯誤如下:
[^2]java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
Sun Dec 18 15:00:34 CST 2016
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest.main(OrderControllerTest.java:96)
Caused by: java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2088)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest$1.call(OrderControllerTest.java:82)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest1.call(OrderControllerTest.java:79)atjava.util.concurrent.FutureTask.run(FutureTask.java:262)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)atjava.util.concurrent.ThreadPoolExecutor1.call(OrderControllerTest.java:79) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutorWorker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
Sun Dec 18 15:00:34 CST 2016
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest.main(OrderControllerTest.java:96)
Caused by: java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest$1.call(OrderControllerTest.java:82)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest1.call(OrderControllerTest.java:79)atjava.util.concurrent.FutureTask.run(FutureTask.java:262)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)atjava.util.concurrent.ThreadPoolExecutor1.call(OrderControllerTest.java:79) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutorWorker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: “”
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
at com.lucky.waimai.api.controller.callback.meituan.order.OrderControllerTest.main(OrderControllerTest.java:96)
Caused by: java.lang.NumberFormatException: For input string: “”
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:453)
at java.lang.Long.parseLong(Long.java:483)

查詢資料,SimpleDateFormat是線程非安全的,顧需要對之前代碼作出改造:

public static void main(String[] args) {
	Callable<Date> task = new Callable<Date>() {
		public Date call() throws Exception {
			DateFormat YYYY_MM_DD_HH_MM_SS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return YYYY_MM_DD_HH_MM_SS.parse("2016-12-18 15:00:34");
		}
	};
	
	// 創建5個線程的線程池
	ExecutorService exec = Executors.newFixedThreadPool(5);
	List<Future<Date>> results = new ArrayList<Future<Date>>();
	for (int i = 0; i < 10; i++) {
		results.add(exec.submit(task));
	}
	exec.shutdown();
	// 輸出結果
	for (Future<Date> result : results) {
		try {
			System.out.println(result.get());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

運行結果如下:
在這裏插入圖片描述
實質上就是每次在線程開始時都要去初始化SimpleDateFormat,這樣每個線程單獨分配空間,就可以避免線程非安全問題。

format方法爲什麼不線程安全
1.有一個共享變量calendar,而這個共享變量的訪問沒有做到線程安全

2.當使用format方法時,實際是給calent共享變量設置date值,然後調用subFormat將date轉化成字符串

解決此類問題還可以有更多的辦法,提供如下,工大家自己實踐:
1.創建一個共享的SimpleDateFormat實例變量,但是在使用的時候,需要對這個變量進行同步

2.使用ThreadLocal爲每個線程都創建一個線程獨享SimpleDateFormat變量

3.需要的時候創建局部變量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章