2014-11-21 43 views
2

我正在對日期格式代碼進行一些重構,因爲我們設法因各種原因引入了許多不一致的地方。我知道最好有一個ThreadLocalSimpleDateFormat。在討論這裏之後,我們不確定在Enum中使用ThreadLocal是否是必需的,因爲我們從不改變實例並且不公開它,因此它不能被突變?如果它是還需要做一個Enum,像這樣,破壞什麼?其他事情是否已經破裂或者沒有做到我認爲應該做的事情?基本上我沒有必須使用ThreadLocal進行很多工作,我不確定這些影響,特別是它與Enum的交互方式。Enum中的ThreadLocal SimpleDateFormat?

public enum DateFormat { 
DATE(newThreadLocalSimpleDateFormat("MM/dd/yyyy")), 
LONG_DATE(newThreadLocalSimpleDateFormat("MMMM dd, yyyy")), 
TIMESTAMP(newThreadLocalSimpleDateFormat("MM/dd/yyyy hh:mm:ss aa")); 

private transient final ThreadLocal<SimpleDateFormat> formatter; 

DateFormat(final ThreadLocal<SimpleDateFormat> formatter) { 
    this.formatter = formatter; 
} 


public String format (final Date date) { 
    return this.formatter.get().format(date); 
} 

private static ThreadLocal<SimpleDateFormat> newThreadLocalSimpleDateFormat(final String frmtString) { 
    return new ThreadLocal<SimpleDateFormat>() { 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(frmtString); 
     } 
    }; 
} 

} 
+2

'SimpleDateFormat'不是線程安全的。事實上,你在枚舉中引用了一個沒有任何區別。 – 2014-11-21 16:05:49

+0

如果你不改變SDF的狀態,那麼你不需要ThreadLocal。 – SMA 2014-11-21 16:08:18

+3

@almas這不夠好。解析方法也修改SDF的狀態。 – 2014-11-21 16:12:07

回答

2

這裏的enum是一個全局常量,它被用作一種方便的方式從threadlocal變量中檢索正確的格式化程序。枚舉是不可變的,但是如果它引用了可變狀態的事物,那麼這些事情仍然會有問題。

當你說

我們永遠不變的情況下,不要暴露它,所以它不能突變

你誤解的線程安全問題的性質。問題是SimpleDateFormat的內部狀態不受多線程訪問的保護,因此當多個線程訪問相同的格式化程序實例時,這些線程中的任何一個都可以改變其他併發線程操縱的狀態,從而破壞結果。

您處理這個格式的選擇是:

  • 離開ThreadLocal的完好,這樣每個線程都有自己的格式複印件;在某些情況下,最大的危險是threadlocal對象可能無法正確清理,因此從池中選擇一個線程(您正在使用線程池,對吧?)可能有一個與以前的使用相關的變量,但在這種情況下,它可能更像是一個特徵:如果所有線程都需要這個,最好是格式化程序附近,

  • 爲每個調用以便不共享任何東西(快速,簡單和線程安全,但是這會在格式化程序丟失時創建垃圾;使垃圾收集器更加努力會降低性能),

  • 同步對格式化程序的訪問,無法同時訪問它(可能是最不具吸引力的選擇,這很可能導致瓶頸)

  • u唱一個不同的DateFormat實現,就像FastDateFormat,它是線程安全的(線程安全可能意味着實現是鎖定或複製狀態,所以可能會有缺點,需要一些測試才能看到結果)。

保留現有的ThreadLocal或簽出線程安全格式化程序的備選方案可能是最佳選擇。保持你擁有的東西看起來是一個安全的選擇。

沒有線程池,Threadlocal變得不那麼吸引人,因爲線程的生命週期更短,因此給定的格式化程序的重用性更低。線程池由於多種原因是一個好主意(主要是確保一個錯誤條件不會導致你的應用程序在線程之外運行,這樣你的應用程序可以以受控的方式降級),如果你不使用它們,你應該。

對我來說,關於這段代碼的最奇怪的事情是枚舉必須是可序列化的,但threadLocal不是可序列化的,所以它必須聲明爲transient。如果這件事沒有得到序列化,並因此出於某種原因反序列化,反序列化的副本將有一個null threadLocal。實際上,這不是你想要序列化的任何東西(你不會將它存儲在HttpSession中或將它傳遞給另一個JVM),因此它看起來像是一個小到不存在的風險。

2

您目前的執行情況良好,您不應通過刪除ThreadLocal來更改它。 SimpleDateFormat不是線程安全的。換句話說,在沒有外部同步的情況下跨線程使用相同的實例將不會產生您期望的行爲。 javadoc對此警告你。

ThreadLocal實例通過爲每個線程提供單個實例來工作。此實例不共享(除非您共享它),因此不會導致任何問題。

事實上,它在enum沒有區別。