[英]Lock vs synchronized vs atomic variables vs volatile in java in terms of read-write atomicity, visibility and protection from reordering
[英]Understanding atomic-ness, visibility and reordering of synchonized blocks vs volatile variables in Java
我試圖從 Java Concurrency in Practice 一書中了解volatile
關鍵字。 我在三個方面比較了synchronized
關鍵字和volatile
變量:原子性、易變性和重新排序。 我對此有一些疑問。 我在下面一一討論過:
書中關於synchronized
可見性說如下:
線程
A
在同步塊中或在同步塊之前所做的一切,當它執行由同一鎖保護的同步塊時,對B
都是可見的。
它說以下關於volatile
變量的可見volatile
:
易失性變量不會緩存在寄存器或緩存中,它們對其他處理器隱藏,因此對易失性變量的讀取始終返回任何線程的最新寫入。
volatile 變量的可見性影響超出了 volatile 變量本身的值。 當線程 A 寫入一個 volatile 變量,隨后線程 B 讀取同一個變量時,在寫入 volatile 變量之前對 A 可見的所有變量的值在讀取 volatile 變量后對 B 可見。 所以從內存可見性的角度來看,寫入一個 volatile 變量就像退出一個同步塊,讀取一個 volatile 變量就像進入一個同步塊。
一季度。 我覺得上面的第二段( volatile
)對應於書中所說的synchronized
。 但是是否有synchronized
相當於volatile
的第一段? 換句話說,使用synchronized
可以確保任何/某些變量不會被緩存在處理器緩存和寄存器中?
請注意,該書還說明了以下有關synchronized
可見性:
鎖定不僅僅是關於互斥; 它還與內存可見性有關。
本書在重新排序的上下文中說明了以下有關volatile
的內容:
當一個字段被聲明為
volatile
,編譯器和運行時會注意到這個變量是共享的,並且對它的操作不應與其他內存操作重新排序。
Q2。 本書沒有提及在synchronized
上下文中重新排序的任何內容。 有人可以解釋一下在synchronized
上下文中重新排序的含義嗎?
本書說以下關於synchronized
和volatile
原子volatile
。
volatile 的語義不足以使增量操作(
count++
)原子化,除非您可以保證變量僅從單個線程寫入。加鎖可以同時保證可見性和原子性; volatile 變量只能保證可見性。
Q3。 我想這意味着兩個線程可以一起看到volatile int a
,兩者都會增加它然后保存它。 但是只有最后一次讀取才會生效,從而使整個“讀取-增量-保存”非原子性。 我對volatile
非原子性的這種解釋是否正確?
第 4 季度。 是否所有鎖定等效項都具有可比性並具有相同的可見性、排序和原子性屬性:同步塊、原子變量、鎖?
PS:這個問題與我幾天前問過的這個問題的完全修改版本有關。 自從全面改造后,我還沒有刪除舊的。 我以更有針對性和結構化的方式寫了這個問題。 一旦我得到這個答案,就會刪除舊的。
'synchronized' 和 'volatile' 之間的主要區別在於,'synchronized' 可以使線程暫停,而 volatile 不能。
“緩存和寄存器”不是一回事。 這本書說,因為在實踐中這通常是事物的實現方式,並且它使理解 JMM(Java 內存模型)的方式和原因變得更容易(或者可能不是,考慮到這些問題)。
然而,JMM 沒有為它們命名。 它只是說 VM 可以自由地為每個線程提供其自己的任何變量的本地副本,或者不,在任意時間與某些或所有其他線程同步,或者不同步......除非有一個happens-before任何地方的關系,在這種情況下,VM必須確保在兩個線程之間的執行點(在關系建立之前發生) ,它們觀察處於相同狀態的所有變量。
實際上,這可能意味着刷新緩存。 或不; 這可能意味着另一個線程覆蓋了它的本地副本。
虛擬機可以隨意實現這些東西,而且在每個架構上都不同。 只要 VM 堅持 JMM 提供的保證,它就是一個很好的實現,因此,您的軟件必須僅在這些保證下工作,而沒有其他假設; 因為如果您依賴 JMM 無法保證的假設,那么在您的機器上工作的內容可能無法在另一台機器上工作。
重新排序也不在 VM 規范中。 VM 規范中的 IS 是以下兩個概念:
在單個線程的范圍內,您可以從內部觀察到的所有內容都與有序視圖一致。 也就是說,如果你寫 'x = 5; y = 10;' 不可能從同一個線程中觀察到 y 是 10 而 x 是它的舊值。 無論是同步的還是不穩定的。 因此,任何時候它都可以在不可見的情況下重新排序事物,那么 VM 就可以自由地進行。 會嗎? 直到 VM。 有些會,有些不會。
當觀察由其他線程引起的效果時,並且您還沒有建立發生在之前的關系,您可能會以任何順序看到一些、全部或沒有這些效果。 真的,這里什么都可能發生。 在實踐中,那么: 不要試圖觀察由其他線程引起的效果而不建立一個發生之前,因為結果是任意的和不可測試的。
發生在各種事物建立關系之前; 同步塊顯然是這樣做的(如果您的線程在嘗試獲取鎖時被凍結,然后它運行,則該對象上完成“之前發生過”的任何同步塊以及它們所做的任何事情您現在都可以觀察到,並保證您觀察與那些按順序運行的東西一致,以及他們寫的所有數據你可以看到的地方(例如,你不會得到一個舊的“緩存”或諸如此類的東西)。易失性訪問也是如此。
是的,即使 x 是 volatile,您對為什么 x++ 不是原子的解釋是正確的。
我不確定你的 Q4 想要問什么。
一般來說,如果你想原子地遞增一個整數,或者執行許多其他並發操作中的任何一個,請查看java.util.concurrent
包。 這些包含各種概念的有效和有用的實現。 例如, AtomicInteger可用於以其他線程可見的方式以原子方式遞增某些內容,同時仍然非常高效(例如,如果您的 CPU 支持比較和設置(CAS)操作,則 Atomicinteger 將使用它; 不是您可以在不求助於Unsafe
情況下從通用 Java 中執行的操作)。
只是為了補充 rzwitserloot 優秀的答案:
A1. 您可以這樣想:一旦第一個線程退出同步塊並且在另一個線程進入之前,synchronized 保證所有已兌現的更改對進入同步塊(從緩存中刷新)的其他線程可見。
A2。 當且僅當 T2 在同一個守衛上同步時,同步塊內的線程 T1 執行的操作在其他線程 T2 看來是未重新排序的。
A3. 我不確定你是怎么理解的。 可能會發生這樣的情況,當增加兩個線程時,首先讀取變量a
將產生一些值v
,然后兩個線程將在本地增加值v
的本地副本,產生v' = v + 1
,然后兩個線程都將寫入v'
來a
。 因此最后的值a
可以是v + 1
,而不是v + 2
。
A4. 基本上是的,雖然在同步塊中您可以原子地執行許多操作,而原子變量允許您只執行某個特定的單個操作,例如原子增量。 此外,不同之處在於,當不正確地使用同步塊時,即通過讀取同步塊外部的變量,這些變量被同步塊內的另一個線程修改,您可以非原子地觀察它們並重新排序。 原子變量不可能實現的東西。 鎖定與同步完全相同。
Q1 。 我覺得上面的第二段(不穩定的)對應於書中關於同步的內容。
當然。 volatile
訪問可視為同步精簡版。
但是是否有同步等效於 volatile 的第一段? 換句話說,使用 synchronized 是否可以確保任何/某些變量不會被緩存在處理器緩存和寄存器中?
這本書通過混合級別使您感到困惑。 volatile
訪問與處理器緩存或寄存器沒有直接關系,事實上這本書關於緩存肯定是不正確的。 波動性和同步性與某些操作的線程間可見性有關,尤其是對共享變量的寫入。 如何語義實現在很大程度上是一個獨立的問題。
無論如何,不,同步不會對變量的存儲施加任何限制。 與同步語義有關的一切都發生在同步區域的邊界上。 這就是為什么從一組並發運行的線程對給定變量的所有訪問都必須在同一對象上同步,以便程序與該變量正確同步。
本書在重新排序的上下文中說明了以下有關
volatile
的內容:當一個字段被聲明為
volatile
,編譯器和運行時會注意到這個變量是共享的,並且對它的操作不應與其他內存操作重新排序。Q2 。 本書沒有提及在同步上下文中重新排序的任何內容。 有人可以解釋一下在同步上下文中重新排序的含義嗎?
但是,這已經不說的同步訪問的東西(不是一切)。 您需要了解,這種意義上的“內存操作”是對共享變量的讀取或寫入,或者獲取或釋放任何對象的監視器。 進入同步區域需要獲取一個監視器,所以書中已經正確地指出,不會跨同步區域的邊界對volatile
訪問進行重新排序。
更一般地,共享變量的讀取不會相對於同步區域的開頭重新排序,並且寫入不會相對於一個區域的結尾重新排序。
Q3 。 我想這意味着兩個線程可以一起看到 volatile int a ,兩者都會增加它然后保存它。 但是只有最后一次讀取才會生效,從而使整個“讀取-增量-保存”非原子性。 我對 volatile 的非原子性的這種解釋是否正確?
是的。 自增運算符對應用它的變量執行讀取和寫入操作。 如果該變量是volatile
則 volatile 語義適用於這些單獨的變量,因此如果沒有其他保護,則可能會在同一變量上發生其他操作。
第 4 季度。 是否所有鎖定等效項都具有可比性並具有相同的可見性、排序和原子性屬性:同步塊、原子變量、鎖?
嗯? 這個子問題太寬泛了。 你正在閱讀關於它的整本書。 一般來說,雖然,沒有這些機制有一些共同的特點和一些不同。 所有這些都會影響內存操作的可見性及其順序,但它們並不完全相同。 “原子性”是另外兩個的函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.