簡體   English   中英

原子操作和多線程

[英]Atomic Operations and multithreading

最近我正在閱讀一個教程,因為我發現了一個聲明說...

“Java語言規范保證了讀取或寫入的變量是一個原子操作(除非該變量的類型的longdouble )類型的操作變量longdouble只有原子的,如果他們與申報volatile關鍵字。”

AtomicIntegerAtomicLong提供getAndDecrement()getAndIncrement()getAndSet()等原子方法。

我對上述陳述感到困惑。你能否澄清何時使用 AtomicIntegerAtomicLong類。

a = 28 (帶有a int )是一個原子操作。 但是,執行a++不是原子操作,因為它需要讀取a的值,增量和寫入結果。 因此,如果您使用a++來實現線程安全計數器,您可以讓兩個線程同時讀取該值(例如26),然后同時增加它並同時寫入它,結果導致27,而不是28。

AtomicInteger通過提供與您列出的原子操作類似的原子操作來解決此問題。 在我的示例中,您將使用incrementAndGet() ,這將保證結束值為28而不是27。

原子意味着操作完成,沒有任何可能發生的事情。 例如。 AtomicInteger上的getAndDecrement()保證變量同時返回和遞減。

如果它不是原子操作,則可能存在值遞減(例如從3到2),然后由另一個線程修改(例如,將其從2更改為5),然后返回為5。

如果需要讀取變量並根據讀取值寫入結果,則需要AtomicInteger 例如, i++讀取i (例如3 )並寫入i+1 (例如4 )。 一個線程可以同時中斷,和其他三個線程遞增i了。 現在我們回來了, i實際上有值6但我們的線程仍然根據它事先讀取的內容寫入4

AtomicInteger.getAndIncrement確保您不會被中斷 ,因此總是正確遞增。 此外,結果總是刷新到內存中 ,而非易失性i可能不會刷新到內存中。 在這種情況下,其他線程甚至可能看不到更改。

我認為這意味着長讀和雙讀操作是原子的,寫操作是原子的。 但讀取+寫入不是原子的。

volatile long num;
num = num+1

以上不是線程安全的。 讀寫是兩個獨立的操作。 每個都保證是原子的,但整個表達不是。

要使其線程安全,您需要使用AtomicLong並使用getAndIncrement函數。

您可以根據要處理的數字范圍的上限/下限使用int或long。 請不要將長原子的非原子行為與AtomicLong混合。 無論你上面寫的是什么都是正確的,但你可能會混合這兩個概念。 在您進行“比較和設置”操作的情況下,AtomicXXX更有用。 例如,即使在多線程環境中代碼在原子上被修改/讀取也是不正確的:

int i =10
..
..
..
if(i == 10) i++;

在多線程環境中,兩個線程可以原子方式訪問此代碼並更新i的值並使其處於一致狀態。 因此,處理這種情況通常會保護代碼“if(i == 10)i ++;” 與同步塊。 但是,AtomicInteger類提供了實現此類功能的API,而不使用速度較慢的同步塊。 AtmoicLong API的情況也是如此

變異變量時需要操作的原子性。 int a = 10; 是一個原子操作,但它不會給你提供問題。 提供操作的問題通常是變異的,如a++a = a + 2; 等等。

Java規范保證“讀取”和“寫入”是原子操作而不是它們的組合。 因此,根據規范,“讀取,添加1然后將結果寫回”的操作不是原子操作。 這些操作稱為復合操作,它們通常需要在我們的代碼中使用它們的上下文。

原子類型有助於解決此問題。 在原子類型上使用incrementAndget()使得'讀取,添加1然后將結果寫回並讀取新結果'在上下文中的單個原子操作到線程安全性。

希望這可以幫助。 順便說一句,你應該閱讀這篇關於並發和線程基礎知識的文章( http://walivi.wordpress.com/2013/08/24/concurrency-in-java-a-beginners-introduction/ )。 它很好地解釋了這些東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM