2012-06-20 60 views
3

在像Android這樣的多線程環境中,一個簡單的int變量可能會被多個線程操縱,在這種情況下,仍然有理由將int用作數據成員嗎?是否有任何理由不總是使用AtomicInteger作爲數據成員?

作爲局部變量的一個int,限於對其進行獨佔訪問的方法的範圍(並且因此開始修改它總是在同一個線程中),這在性能上是非常有意義的。

但是,作爲數據成員,即使包含訪問者,它也可能會遇到衆所周知的併發交錯修改問題。

所以它看起來像「玩它安全」,可以全面使用AtomicInteger。但這似乎非常低效。

你能舉一個線程安全的int數據成員用法的例子嗎?

+0

對int的操作是不是原子的? (誠​​實的問題,這對我來說是一個新話題。) – djechlin

+0

@djechlin即使是'++'也不是原子的。 – ef2011

+1

'++'不是原子的,但讀取是寫入(賦值)。換句話說,你永遠不會得到交錯的字節。 '++'的問題是'i = i + 1'的簡寫,並且在讀和賦值之間可能會有一個突變。 – pamphlet

回答

8

是否有任何理由不總是使用AtomicInteger作爲數據成員?

是的,有很好的理由總是用AtomicInteger。由於volatile構造比本地int和其他Unsafe構造被用於設置/獲得底層int值,因此可能至少比一個數量級慢(可能更多)。 volatile意味着您在訪問AtomicInteger時每隔越過內存障礙,這會導致相關處理器上的高速緩存內存刷新。

此外,正因爲您已將所有字段設置爲AtomicInteger,所以在訪問多個字段時不會保護您免受競爭條件的影響。關於何時使用volatile,​​和Atomic*類別做出正確的決定是無可替代的。

例如,如果你在,你想在一個可靠的方式來訪問一個線程程序類有兩個字段,那麼你會做這樣的事情:

synchronized (someObject) { 
    someObject.count++; 
    someObject.total += someObject.count; 
} 

如果這兩個部件對AtomicInteger然後你會訪問volatile兩次,所以跨越2個內存屏障而不是1個。另外,分配比在AtomicInteger內部的Unsafe操作更快。另外,由於兩個操作的數據競爭條件(與上面的​​塊相對),您可能無法獲得total的正確值。

你能舉一個線程安全的int數據成員用法的例子嗎?

從使它final

除了,存在一種用於除了將其標記爲volatile,或使用AtomicInteger線程安全int數據成員的機制。沒有什麼奇妙的方法可以在所有字段上繪製線程安全。如果有的話,線程編程將很容易。挑戰是找到合適的地方放置您的​​區塊。找到應該標記爲volatile的正確字段。找到合適的地方使用AtomicInteger和朋友。

+0

好的。那麼,如何保證'int'數據成員的類的線程安全? – ef2011

+0

你必須在課堂上正確「同步」。請參閱http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html – Gray

+0

是的,但沒有鎖定*整個類*效率更低? – ef2011

0

這取決於它如何使用wrt。其他數據。一個類封裝了一個行爲,所以如果沒有其他變量,變量幾乎是毫無意義的。在這種情況下,保護(*)屬於一起的數據成員(或整個對象)可能會更好,而不僅僅是一個整數。如果你這樣做,那麼AtomicInteger是使用常見的線程安全機制不必要的性能影響

(*):互斥,信號,監控等

1

如果你有effecitvely不變int是你可以逃脫不以計算爲代價確保同步。一個例子是hashCode

int hash = 0; 

public int hashCode(){ 
    if(hash == 0){ 
    hash = calculateHashCode(); //needs to always be the same for each Object 
    } 
    return hash; 
} 

這裏最明顯的權衡是相同的散列值多重計算的可能性,但如果選擇是一個​​的hashCode能夠具有影響差遠了。

這在技術上是線程安全的,儘管是多餘的。

+0

...這不是線程安全的嗎?線程1做hash == 0,線程2做hash == 0,1計算並返回,2計算並返回。不同的值來自相同的hashCode。 – djechlin

+0

除了重複的計算,你會產生什麼副作用? –

+0

在線程環境中,hashCode可以在不同的調用中返回不同的值,但在非線程環境中這不是真的。 – djechlin

0

線程安全不僅是關於atomic int賦值,您需要仔細設計您的鎖定模式以獲得代碼中的一致性。

如果您有兩個Account類與公共數據成員Balance請考慮以下簡單的代碼。

Account a; 
... 
int withdrawal = 100; 
if(a.Balance >= withdrawal) 
{ 
    // No atomic operations in the world can save you from another thread 
    // withdrawing some balance here 
    a.Balance -= withdrawal 
} 
else 
{ 
    // Handle error 
} 

真的很坦率。在現實生活中,原子分配很難解決我的現實生活中的併發問題。

相關問題