2013-03-18 202 views
1

這個Java類是線程安全的嗎?是長線程安全嗎?

class Counter() { 

    private Long counter = 0; 

    Long get() { return counter; } 

    Long inc() { return ++counter; } 
} 

如果沒有,是否有可能使線程安全的,而無需使用鎖明確(或​​關鍵字)?如果不是,那麼我猜以下是實現我的目標的最簡單方法嗎?

class Counter() { 

    private final AtomicLong counter = new AtomicLong(0); 

    Long get() { return counter.get(); } 

    Long inc() { return counter.incrementAndGet(); } 
} 
+2

你的第一個例子,除了不可編譯之外,不會做你認爲它的工作。 – Perception 2013-03-18 13:46:52

+1

我假設你的意思是'++ counter',它不應該因爲'final'而被編譯。對? – Gray 2013-03-18 13:47:35

+0

如果您刪除語法錯誤並將變量名從'long'更改爲'counter',它確實不會因爲'final'而編譯,是的。 – Perception 2013-03-18 13:48:49

回答

8

不,第一個例子是不是線程安全的,因爲++counter不是原子。例如,沒有什麼可以阻止兩個線程同時執行++counter並丟失其中一個增量。

第二個示例線程安全的,這意味着沒有增量會丟失。值得注意的是,get()inc()返回一個值,在調用者接收到該值時可能會過時。

+1

他的代碼說'++長'這是不對的,'++計數器'不應該與最終的編譯,對不對? – Gray 2013-03-18 13:48:31

+0

@格雷:是的,這些是我提到的明顯錯誤:) – NPE 2013-03-18 13:49:01

0

Long是不可變的,那麼它是線程安全。什麼是不是線程安全是由inc,它(正確實施)要求拆箱增量和指派等同於:

Long inc() { return counter= long.longValue()+1; } 
2

關於線程safey和多頭了不同的答案。 Java中很長的一個是64位,它佔用兩個獨立的32位寄存器。你有高32位和低32位。單個寫入佔兩個非原子32位存儲。

您可以以一個線程的高32位寫入和另一個線程的低32位寫入結束,這可能會產生一個既不是來自第一個線程也不是第二個線程的數字。

+0

雖然這是一個問題,在這種情況下 - 終於編輯 - 它不是。 OP使用「長」而不是「長」。這個實例變量必須在要增加的方法中取消裝箱,然後在存儲回實例變量之前將其重新裝箱。實際增加的'long'只出現在調用方法的操作數堆棧中。 – parsifal 2013-03-18 14:27:34

+0

@Parisfal同意在這種情況下,它可能不是一個問題,但一般來說,要了解線程安全性和長/長是一個問題。無論長或長,增加的線程安全失敗發生有或沒有拳擊。 – 2013-03-18 14:36:00

+0

@Parisfal我知道'長'不是'線程安全',那麼'長'怎麼樣? – 2016-11-20 05:26:44