2016-09-23 53 views
0

我見過的ThreadLocal的每個實例返回不能動態地設置,類似這樣的例子有SimpleDateFormat的,每次它總是返回相同的SimpleDateFormat值:ThreadLocal的初始化

public class Foo 
{ 
    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() 
     { 
      return new SimpleDateFormat("yyyyMMdd HHmm"); 
     } 
    }; 

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

但可以說,我希望能夠配置返回的值。一種方法是使用系統屬性是這樣的:

public class Foo 
{ 
    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() 
     { 
      String dateFormat = System.getProperty("date.format"); 
      return new SimpleDateFormat(dateFormat); 
     } 
    }; 

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

但是,如果我不希望使用系統屬性,而是想用它創建時的必要信息提供類的東西。我怎麼做。一切都是靜態的,所以我不能使用構造函數。

我不喜歡系統屬性方法的原因很多。對於一個我不想讓這個班級瞭解其周圍環境的東西,那就是應該閱讀的系統屬性。它應該儘可能簡單,並注入所有的依賴關係。例如,我認爲這種編碼方式可以提高可測性。

最終解決

格式是通過調用setFormat和formatIt所有來電設置一次後,使用相同的格式。

public class Foo { 

    private static volatile String FORMAT; 

    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() { 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(FORMAT); 
     } 
    }; 

    /** 
    * Set the format. Must be called before {@link #formatIt(Date)}. Must only be called once. 
    * 
    * @param format 
    *   a format, e.g. "yyyyMMdd HHmm". 
    * @throws IllegalStateException 
    *    if this method has already been called. 
    */ 
    public static void setFormat(String format) { 
     if (Foo.FORMAT != null) { 
      throw new IllegalStateException("Format has already been set"); 
     } 
     FORMAT = format; 
    } 

    /** 
    * @return the formatted date. 
    * @throws IllegalStateException 
    *    if this method is called before {@link #setFormat(String)} has been called. 
    */ 
    public static String formatIt(Date date) { 
     if (Foo.FORMAT == null) { 
      throw new IllegalStateException("Format has not been set"); 
     } 
     return formatter.get().format(date); 
    } 

} 
+0

你用java 8嗎? –

+0

不,我使用Java 1.6。 – Mattias

+0

Wy的投票?我對此進行了廣泛的研究,並且我是一位經驗豐富的程序員。這不像我在學校,只是想讓你們幫我完成作業。這將用於生產。如果給我棄權的人認爲這是一個微不足道的問題,請提供答案。 – Mattias

回答

2

當你不使用DI框架,對我來說唯一的辦法就是增加一個static二傳手允許動態改變您的格式,這樣的事情:

public class Foo { 
    private static volatile String FORMAT = "yyyyMMdd HHmm"; 

    // SimpleDateFormat is not thread-safe, so give one to each thread 
    private static final ThreadLocal<SimpleDateFormat> formatter = 
     new ThreadLocal<SimpleDateFormat>(){ 
     @Override 
     protected SimpleDateFormat initialValue() { 
      return new SimpleDateFormat(FORMAT); 
     } 
    }; 

    public static void setFormat(String format) { 
     FORMAT = format; 
    } 
    ... 
} 

這是很醜陋但我沒有看到更好的方法。


在這個特殊的使用情況下,當你使用Java 6,我顯然會建議使用的Joda-Time,而不是SimpleDateFormat,因爲它做同樣的事情DateTimeFormatter,它是線程安全的開箱即用。

這裏是你的代碼會是什麼樣子:

public class Foo { 

    private static volatile DateTimeFormatter formatter = 
     DateTimeFormat.forPattern("yyyyMMdd HHmm");; 

    public String formatIt(Date date) { 
     return formatter.print(date.getTime()); 
    } 

    public static void setFormat(String format) { 
     formatter = DateTimeFormat.forPattern(format); 
    } 
} 

正如你可以看到的是沒有更多的需要ThreadLocalDateTimeFormatter是線程安全的。

注:我認爲你需要設置格式全球Foo所有情況下,如果不是的話,你倒是應該這樣做:

public class Foo { 

    private volatile DateTimeFormatter formatter = 
     DateTimeFormat.forPattern("yyyyMMdd HHmm");; 

    public String formatIt(Date date) { 
     return formatter.print(date.getTime()); 
    } 

    public void setFormat(String format) { 
     this.formatter = DateTimeFormat.forPattern(format); 
    } 
} 

用這種方法你注入更多面向對象方法的格式。

+0

這實際上比以前簡單得多,當我累了的時候我不應該考慮threadlocals。 – Kayaman

+0

@Kayaman你應該考慮休息一下然後:-) –

+0

@Nicolas謝謝你的建議解決方案。但是,這種解決方案與以前由Kayaman提出的解決方案不同嗎? – Mattias