2012-09-03 79 views
14

據我所知java.util.Date是可變的,所以如果多線程試圖訪問和修改它,它就不是線程安全的。我們如何使用客戶端鎖定或組合(包裝)來使其線程安全?如何使Java.util.Date線程安全

+4

如果我們已經在這裏,'GregorianCalendar'和'SimpleDateFormat'也不是線程安全的。總是值得提醒。 –

+0

感謝提醒 – peter

回答

28

按照此順序,從最好到最差:

  1. 不使用它在所有的,看看

  2. 不使用它在所有使用AtomicLong或一成不變的原始longvolatile代表時代

  3. 封裝它。總是返回Date的防禦副本,從不參考內部對象

  4. 同步於Date實例。

+0

我想知道你爲什麼認爲第二個比第三個好? – peter

+2

@ user1389813:好問題! 1.基元是不可變的,因此隱式線程安全。來自「簡明英漢詞典」巧合的是,你不能重新引用內部對象而不是防禦性副本。 3.更輕量,更少複製(不是很重要)。但我同意,2和3都很好。同樣顯而易見的是'Date'具有比'long'更好的語義。 –

+0

我明白了。第二點你需要守衛它與鎖權利?因爲@dystroy指出增量操作不是原子的。 – peter

-1

沒有簡單的解決方案來創建Date類的線程安全封裝。最好的方法是使用​​塊同步它的所有對象。

+0

然後你需要攜帶這個同步塊,無論你使用它。這在實踐中不是很好嗎? – peter

+0

代碼很糟糕。這就是爲什麼Tomasz的回答比我的要好得多;) –

2

最簡單的解決方法是永遠不要修改日期並永遠不會共享它。即只對本地變量使用日期。

您可以使用JodaTime,因爲它具有不可變的日期對象。

3

您可以使用long值(Epoch以來的毫秒數)而不是Date實例。分配它將是一個原子操作,它始終是連貫的。

但是,您的問題可能不是日期值本身,而是整個算法,這意味着真正的答案將基於您真正的問題。

這裏的越野車運行在多線程上下文中的例子:

long time; 
void add(long duration) { 
    time += duration; 
} 

這裏的問題是,你可能有導致只有一個有效的另外兩個平行的增加,因爲time += duration不是原子(它真的time=time+duration)。

使用long而不是可變對象是不夠的。在這種情況下,您可以通過將函數設置爲同步來解決問題,但其他情況可能會更棘手。

+0

你的意思是'你的問題可能不在數據值本身上,而是在整個算法上 – peter

+0

如果你'重新開始閱讀日期並通過替換值完成長時間的操作。在操作過程中鎖定值可能會做到這一點。 –

+0

它會幫助,如果它變得易變? – peter