1. 程式人生 > >Java volatile使用詳細說明-可見性的詳細講解

Java volatile使用詳細說明-可見性的詳細講解

上篇我們一起學習了同步的各種騷操作,本篇來來看下同步狀語從句:齊名的揮發性,不管你聽到的是同步的一種弱形式也好,還是聽到的最輕量的同步機制,都是java的研究者給予揮發性關鍵字的美譽。

 

本文要點:

1,同步和揮發性的特性差異

2,使用易失性需要滿足的條件

3,易失性特性在虛擬機器層面的實現

4,易失性示例程式碼

 

1,同步和揮發性的特性差異

 

1.1, synchronized保證可見性和原子性,而volatile僅保證可見性。原子性大家應該都知道了,關於可見性,是指當一個執行緒修改了被揮動修飾的變數後,新值對於其他執行緒來說,是可以立即得知的原理見下文。

 

1.2, synchronized可能會造成執行緒的阻塞,而volatile不會阻塞;

 

1.3,當共享資料的改變依賴於該資料之前的值時,同步可以保證執行緒

安全性,而揮發性不行.volatile僅適用於共享變數作為獨立的狀態開關時的執行緒安全性,如標識完成,中斷等的標記使用(根本原因是揮發性僅保證可見性)。

 

1.4, synchronized修飾方法或程式碼塊,volatile修飾變數,且被揮發修飾的變數不會參加重排序;

 

在虛擬機器的之前發生規則中,有一條是關於揮發性的,如下:

 

volatile變數規則(Volatile Variable Rule):對一個volatile變數的寫操作先行發生於後面對這個變數的讀操作,這裡的“後面”同樣是指時間上的先後順序。

 

 

而關於之前發生規則,也就是先行規則,是指java的記憶體模型中定義的兩項操作之間的偏序關係,如果操作一個在乙之前發生,那麼操作阿產生的影響應該能被操作乙觀察到,影響包括很多,比如改變了共享變數的值,傳送了訊息,進行了方法呼叫以及返回了值等。對之前發生規則感興趣的話,可以自行了解下java的記憶體模型的這塊。

 

2,使用易失性需要滿足的條件

 

2.1,該變數的修改不依賴變數的當前值或能確保只有單一執行緒能修改該變數值;

 

2.2,變數不需要和其他變數共同參與不變約束;

 

注:關於不變約束,簡單的說就是1 + 2 = 3時,不管在順序執行程式中,還在多執行緒執行程式中,1 + 2都應該等於3,而不應該等於其他。

 

2.3,訪問變數時,沒有其他的原因需要加鎖;

 

3,易失性特性在虛擬機器層面的實現

 

虛擬會保障以一種可預見的方式告知其他執行緒被揮發性修飾的變數的更新,即,如果被揮發性修飾的變數被一個執行緒甲更新,那麼執行緒甲工作記憶體中更新後的值會直接刷到主記憶體中,當其他執行緒需要用到該變數時,發現該變數是被易失性修飾,即使其他執行緒的工作記憶體中有該變數的副本值,也會直接從主記憶體中載入該變數的值進行使用。作為對比,我們看下普通變數在各個執行緒中的使用方式,普通變數在多執行緒環境下,也會在各個執行緒的工作記憶體中儲存著一份執行緒的副本,但各個執行緒在使用該普通變數時,僅會關注各自工作記憶體中的副本值,而不會主動進行資料的通訊和同步。

 

4,易失性示例程式碼

 

4.1,是否使用揮發性修飾的共享變數

如下圖所示:

上面兩個圖程式碼,在服務端模式下執行時,會在打印出“下面終止執行”後也一直列印“執行中......”。

注:讀者在自己電腦下執行時可能會在打印出 “下面終止執行” 便真的終止了,出現這種情況的原因是自己的虛擬機器並非在伺服器模式下執行的。

當如下圖中所示,加了紅線上放的揮發後,則會正常的終止列印了。

4.1,使用揮發性修飾的共享的變數自增

如下面兩個圖所示:

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號了隨想或掃描下方二維碼關注檢視:

列印結果如下:可見並不能正常的進行自增操作,按我們的預期計數值應該是100 * 100,但實際結果卻並非如我們預期。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號了隨想或掃描下方二維碼關注檢視:

 

本篇完。

歡迎大家關注我的公眾號號“up隨想”!!