簡體   English   中英

這是制作java.text.DateFormat threadSafe的正確方法嗎?

[英]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,並且具有線程安全的格式化程序。

另一種選擇是只有兩個靜態方法, parseMonthDayYearformatMonthDayYear ,讓它們處理線程安全。

您可以使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM