[英]Is this is a right way to make java.text.DateFormat threadSafe?
我正在開發一個項目,其中日期格式保存為靜態實用程序字段,如下所示
public static final SimpleDateFormat MM_DD_YYYY = new SimpleDateFormat(DATE_FORMAT_DEFAULT, Locale.US);
幸運的是,FindBugs開始發出警告,即DateFormats對於多線程使用本質上是不安全的。
為了刪除這些警告,團隊中的一個開發人員將字段訪問權限更改為私有,並提供了這樣的公共訪問者功能
private static final SimpleDateFormat MM_DD_YYYY = new SimpleDateFormat(DATE_FORMAT_DEFAULT, Locale.US);
public static SimpleDateFormat getMmDdYyyy() {
return MM_DD_YYYY;
}
神奇的FindBugs警告消失了! 我的問題是,兩個代碼片段在語義上是否相同?
如果是,為什么Findbugs在第二種情況下沒有顯示任何警告?
在thead安全上下文中使用DateFormat的最好和更快的方法是在ThreadLocal中使用它。 它會為每個線程確保一個實例。
像這樣 :
private ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat> () {
@Override
public DateFormat get() {
return super.get();
}
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy MM dd");
}
@Override
public void remove() {
super.remove();
}
@Override
public void set(DateFormat value) {
super.set(value);
}
};
public Date convertStringToDate(String dateString) throws ParseException {
return df.get().parse(dateString);
}
請查看此鏈接以獲取更多詳細信息和基准: http : //www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html
好吧,公共領域幾乎總是一個壞主意,特別是對於可變對象,這樣做更好 。
但是,它絕對不是線程安全的。 兩個線程絕對可以調用該方法並最終同時使用格式化程序。
您可以創建自己的包裝器對象(可能仍在擴展DateFormat
;我不確定是否隨意)哪些序列化請求並且可能具有底層格式的“池”。 或者您可以使用Joda Time或java.time
,這兩者都是更好的日期/時間API,並且具有線程安全的格式化程序。
另一種選擇是只有兩個靜態方法, parseMonthDayYear
和formatMonthDayYear
,讓它們處理線程安全。
您可以使用ThreadLocal:
private static ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(DATE_FORMAT_DEFAULT, Locale.US);
}
};
SimpleDateFormat
不是線程安全的,因為它在處理時使用內部可變狀態。 使其成為final static
將無濟於事,因為仍將對該單個實例執行操作,其中通過該狀態線程將進行交互。
解決方案:返回防御性副本:
private static final DateFormat MM_DD_YYYY = new SimpleDateFormat(DATE_FORMAT_DEFAULT, Locale.US);
public static DateFormat getMmDdYyyy() {
return (DateFormat) MM_DD_YYYY.clone();
}
這只是一個實驗,但我想知道是否有一個額外的解決方案可能是創建一個SimpleDateFormat
子類來覆蓋所有使用no-op實現的set*()
方法,並使用它? 在我看來, SimpleDateFormat
線程安全問題來自格式的可變性。 (我無法想象在實際的format()
調用過程中有任何狀態。)因此,如果你使用的是一個你知道永遠不會改變的SimpleDateFormat
,那么可以使用這個子類嗎? 你甚至可以在構造函數中傳遞一個SimpleDateFormat
實例。 這將是一個不可變的裝飾器,你可以使用java.util.Collections.unmodifiable*
方法。
加分是你不必確保每個線程有一個實例,或者有同步開銷。 減號(我認為)是這可能不滿足FindBugs(我沒有它所以我不能嘗試它。)
事實證明這不起作用。 如@biziclop在下面的評論指出,實際上是內使用可變內部狀態format()
方法-的共享實例Calendar
。 這是 @biziclop提供的鏈接 ,顯示了SimpleDateFormat
源代碼的內容。 我不會推測為什么實現者這樣做,但我可以肯定地說,即使format()
是有狀態的,因此我的計划也行不通。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.