在java線程併發處理中,有一個關鍵字volatile的使用目前存在很大的混淆,覺得使用這個關鍵字,在進行多線程併發處理的時候就能夠萬事大吉。java
Java語言是支持多線程的,爲了解決線程併發的問題,在語言內部引入了 同步塊 和 volatile 關鍵字機制。編程
synchronized 安全
同步塊你們都比較熟悉,經過 synchronized 關鍵字來實現,全部加上synchronized 和 塊語句,在多線程訪問的時候,同一時刻只能有一個線程可以用多線程
synchronized 修飾的方法 或者 代碼塊。併發
volatilejvm
用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值。volatile很容易被誤用,用來進行原子性操做。優化
要使 volatile 變量提供理想的線程安全,必須同時知足下面兩個條件:spa
實際上,這些條件代表,能夠被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。 線程
第一個條件的限制使 volatile 變量不能用做線程安全計數器。雖然增量操做(x++
)看上去相似一個單獨操做,實際上它是一個由讀取-修改-寫入操做序列組成的組合操做,必須以原子方式執行,而 volatile 不能提供必須的原子特性。實現正確的操做須要使 x
的值在操做期間保持不變,而 volatile 變量沒法實現這點。(然而,若是將值調整爲只從單個線程寫入,那麼能夠忽略第一個條件。) code
大多數編程情形都會與這兩個條件的其中之一衝突,使得 volatile 變量不能像 synchronized
那樣廣泛適用於實現線程安全。清單 1 顯示了一個非線程安全的數值範圍類。它包含了一個不變式 —— 下界老是小於或等於上界。
一旦一個共享變量(類的成員變量、類的靜態成員變量)被 volatile 修飾以後,那麼就具有了兩層語義:
在java線程併發處理中,有一個關鍵字volatile的使用目前存在很大的混淆,覺得使用這個關鍵字,在進行多線程併發處理的時候就能夠萬事大吉。
Java語言是支持多線程的,爲了解決線程併發的問題,在語言內部引入了 同步塊 和 volatile 關鍵字機制。
synchronized
同步塊你們都比較熟悉,經過 synchronized 關鍵字來實現,全部加上synchronized 和 塊語句,在多線程訪問的時候,同一時刻只能有一個線程可以用
synchronized 修飾的方法 或者 代碼塊。
volatile
用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值。volatile很容易被誤用,用來進行原子性操做。
要使 volatile 變量提供理想的線程安全,必須同時知足下面兩個條件:
實際上,這些條件代表,能夠被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。
第一個條件的限制使 volatile 變量不能用做線程安全計數器。雖然增量操做(x++
)看上去相似一個單獨操做,實際上它是一個由讀取-修改-寫入操做序列組成的組合操做,必須以原子方式執行,而 volatile 不能提供必須的原子特性。實現正確的操做須要使 x
的值在操做期間保持不變,而 volatile 變量沒法實現這點。(然而,若是將值調整爲只從單個線程寫入,那麼能夠忽略第一個條件。)
大多數編程情形都會與這兩個條件的其中之一衝突,使得 volatile 變量不能像 synchronized
那樣廣泛適用於實現線程安全。清單 1 顯示了一個非線程安全的數值範圍類。它包含了一個不變式 —— 下界老是小於或等於上界。
一旦一個共享變量(類的成員變量、類的靜態成員變量)被 volatile 修飾以後,那麼就具有了兩層語義:
1)保證了不一樣線程對這個變量進行操做時的可見性,即一個線程修改了某個變量的值,這新值對其餘線程來講是
當即可見的。
2)禁止進行指令重排序。
volatile 本質是在告訴 jvm 當前變量在寄存器(工做內存)中的值是不肯定的,須要從主存中讀取;
synchronized 則是鎖定當前變量,只有當前線程能夠訪問該變量,其餘線程被阻塞住。
1.volatile 僅能使用在變量級別;
synchronized 則可使用在變量、方法、和類級別的
2.volatile 僅能實現變量的修改可見性,並不能保證原子性;
synchronized 則能夠保證變量的修改可見性和原子性
3.volatile 不會形成線程的阻塞;
synchronized 可能會形成線程的阻塞。
4.volatile 標記的變量不會被編譯器優化;
synchronized 標記的變量能夠被編譯器優化