2012-01-18 50 views
2

我使用的對象,作爲一個包裝,它粗略用於在地圖字符串的一個:我不在線程安全?

public class Wrapper { 
    private Map<String, String> values; 
    private Formatter formatter; 

    public BigDecimal getSpecialValue() { 
    String result = values.get("Special"); 
    return formatter.formatNumber(result); 
    } 
} 

上述的格式化大致用作構造SimpleDateFormat

public class Formatter { 
    private static final NumberFormat NUMBER_FORMAT; 

    public BigDecimal formatNumber(String s) { 
    Number num = NUMBER_FORMAT.parse(s); 
    if (num instanceof Integer) { 
     return new BigDecimal((Integer) num); 
    } else if (num instanceof Double) { 
     return new BigDecimal((Double) num); 
    } ... 
    } 
} 

映射器當我訪問方法一次由幾個線程執行,一些行爲發生,這隻能通過併發訪問來解釋,例如可能有一個NumberFormatException或一個ParseException,其中解析的字符串是.430.430,而不是.430等等。

有兩個方面引起我的興趣: 1.)只能以只讀方式訪問包裝。雖然訪問收藏不同步,但我的印象是這應該始終有效。 2.)在第一次嘗試找到問題時,我更改了Wrapper類的構造函數以執行formatNumber方法(顯然是單線程的),該方法消除了執行中的所有異常。

有人可以解釋一下嗎?

編輯: Wrapper類中的地圖填充在構造函數中,該構造函數絕大多數情況下都是單線程的。之後,包裝的設計使地圖是不可變的。

回答

9

只是在尋找,「線程」的jdoc會找到你NumberFormat類以下內容:Number formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

+2

根據上述評論,請使用一個ThreadLocal <的NumberFormat> – Nate 2012-01-18 13:54:56

+0

@Nate是的顯而易見的解決方案(並會在第一時間對我來說永遠使用'ThreadLocal' - !這給出了被異國情調的加分)。儘管取決於訪問頻率等等。爲每個線程創建一個新實例(如果僅使用一次)可能比某些鎖定效率低。 – Voo 2012-01-18 13:57:07

+0

理解並接受。我沒有閱讀javadoc,但是我正在瀏覽NumberFormat(和子類)源代碼,並且沒有發現任何可以解釋爲什麼NumberFormat不是線程安全的成員變量。即使其成員變量是以只讀方式訪問的,類是不是線程安全的? – Jonathan 2012-01-18 14:06:55

1
  1. 的NumberFormat不是線程安全的。因此將其存儲在一個靜態變量中並從多個線程使用它將導致問題。既可以同步該對象的每次使用,也可以將其存儲在ThreadLocal變量中。

  2. 即使只能以只讀方式訪問包裝器,也必須有一些時間值的映射被初始化和填充,以及創建格式化器的位置,否則不會有任何格式化。你沒有展示真正完整的課程代碼,所以你可能至少在這個課堂上有可見性問題。