2015-01-21 222 views
2

我試圖在Java中爲日期和數字實現格式化程序。但是一些java中的格式化程序不是線程安全的,例如。 (首先,我不明白他們爲什麼不是像DateTimeFormat那樣的線程安全!)所以,在搜索了一下之後,我發現了ThreadLocal變量。如果我在工廠類中實現ThreadLocal會發生什麼

我見過的所有片段都是ThreadLocal,他們使用final。當然,有一個格式化程序實例是有意義的。但是,可以說我們需要一個格式化程序,但是需要3個模式。

FormatFactory.java

public class FormatFactory { 
    public static ThreadLocal<DecimalFormat> getMoneyFormatter(final String pattern) { 
    return new ThreadLocal<DecimalFormat>() { 
     @Override 
     public DecimalFormat initialValue() { 
     DecimalFormat decFormat = new DecimalFormat(pattern); 
     DecimalFormatSymbols symbols = new DecimalFormatSymbols(); 
     symbols.setDecimalSeparator(','); 
     if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) { 
      symbols.setGroupingSeparator('.'); 
     } 
     decFormat.setMinimumFractionDigits(2); 
     decFormat.setDecimalFormatSymbols(symbols); 
     return decFormat; 
     } 
    }; 
    } 
} 

Format.java

public static String money(BigDecimal amount, String pattern) { 
    return FormatFactory.getMoneyFormatter(pattern).get().format(amount); 
} 

使用

Format.money(balance, FormatPatterns.MT940_DECIMAL) 
Format.money(balance, FormatPatterns.SIGNED_MONEY) 
Format.money(balance, FormatPatterns.MONEY) 

這還算是線程安全的我這個用法?

UPDATE:

答案here已經解決了我的問題。

我的片段是如下:

private static final ConcurrentMap<String, ThreadLocal<DecimalFormat>> decimialFormatsByPattern = new ConcurrentHashMap<String, ThreadLocal<DecimalFormat>>(); 

public static DecimalFormat getMoneyFormatter(final String pattern) { 
    ThreadLocal<DecimalFormat> decimalFormatter = decimialFormatsByPattern.get(pattern); 
    if (decimalFormatter == null) { 
     decimalFormatter = new ThreadLocal<DecimalFormat>() { 
     @Override 
     public DecimalFormat initialValue() { 
      DecimalFormat decFormat = new DecimalFormat(pattern); 
      DecimalFormatSymbols symbols = new DecimalFormatSymbols(); 
      symbols.setDecimalSeparator(','); 
      if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) { 
      symbols.setGroupingSeparator('.'); 
      } 
      decFormat.setMinimumFractionDigits(2); 
      decFormat.setDecimalFormatSymbols(symbols); 
      return decFormat; 
     } 
     }; 
     decimialFormatsByPattern.putIfAbsent(pattern, decimalFormatter); 
    } 

    return decimalFormatter.get(); 
    } 

使用

public static String money(BigDecimal amount, String pattern) { 
    return FormatFactory.getMoneyFormatter(pattern).format(amount); 
    } 
+1

卡亞曼說:「......資源泄漏」。如果某個線程t創建一個DecimalFormat實例(或任何其他對象)並將其放在一個ThreadLocal對象中,那麼即使在線程t死後,ThreadLocal對象也將繼續保存該引用。線程t創建的對象永遠不會被垃圾收集。當你擁有一組固定的線程時,ThreadLocal很好用,但它可能會導致在不斷創建新的短暫線程的應用程序中出現問題。 (注意:當請求隊列中的積壓更改時,某些ExecutorService實現會創建並銷燬線程。) – 2015-01-21 17:55:12

回答

2

現在你回到每getMoneyFormatter被調用時,一個新的ThreadLocal。您只應初始化一次。

但是,使用ThreadLocal可能會導致資源泄漏,所以除非您確實知道需要它,否則在需要時創建新的格式化程序會更簡單。

0

它將是線程安全的,因爲在getMoneyFormatter方法中,您將在每次調用getMoneyFormatter方法時創建ThreadLocal的特定實例。

但是這不是ThreadLocal的正確用法。您應該只初始化一次,它應該用於您想要存儲特定於某個線程的變量的位置,然後同一線程稍後可以從ThreadLocal中獲取該值。 ThreadLocal中的存儲值僅對該線程可見,其他線程無法修改或更改。

在你的場景中,如果DecimalFormat是方法getMoneyFormatter的本地對象,那麼它也將是線程安全的,在這種情況下你不需要ThreadLocal.Please檢查下面的例子。

public DecimalFormat getMoneyFormatter(final String pattern) { 

     DecimalFormat decFormat = new DecimalFormat(pattern); 
     DecimalFormatSymbols symbols = new DecimalFormatSymbols(); 
     symbols.setDecimalSeparator(','); 
     if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) { 
      symbols.setGroupingSeparator('.'); 
     } 
     decFormat.setMinimumFractionDigits(2); 
     decFormat.setDecimalFormatSymbols(symbols); 
     return decFormat 

    } 



    public static String money(BigDecimal amount, String pattern) { 
     return FormatFactory.getMoneyFormatter(pattern).format(amount); 
    } 
+0

當前我正在使用該項目中的幾個線程。所以DecimalFormat必須使用ThreadLocal進行線程安全。 – 2015-01-21 14:32:32

+0

方法本地對象始終是線程安全的... – ssood9 2015-01-21 16:08:35

相關問題