Java volatile 從入門到精通

上篇我們一起學習了synchronized的各種騷操作,見synchronized的各種騷操作騷要點-對象鎖、類鎖、this鎖、非this鎖等 本篇來來看下和synchronized齊名的volatile,不管你聽到的是同步的一種弱形式也好,還是聽到的最輕量的同步機制,都是java研究者給予volatile關鍵字的美譽。

Java volatile 從入門到精通


本文要點:

1、synchronized和volatile的特性差異

2、使用volatile需要滿足的條件

3、volatile特性在虛擬機層面的實現

4、volatile示例代碼

1、synchronized和volatile的特性差異

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

1.2、synchronized可能會造成線程的阻塞,而volatile不會阻塞;

1.3、當共享數據的改變依賴於該數據之前的值時,synchronized可以保證線程

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

1.4、synchronized修飾方法或代碼塊,volatile修飾變量,且被volatile修飾的變量不會參加重排序;

在虛擬機的happens-before規則中,有一條是關於volatile的,如下:

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

而關於happens-before規則,也就是先行規則,是指java內存模型中定義的兩項操作之間的偏序關係,如果操作A在B之前發生,那麼操作A產生的影響應該能被操作B觀察到,影響包括很多,比如改變了共享變量的值,發送了消息、進行了方法調用以及返回了值等。對happens-before規則感興趣的話,可以自行了解下java內存模型的這塊。

2、使用volatile需要滿足的條件

2.1、該變量的修改不依賴變量的當前值或能確保只有單一線程能修改該變量值;

2.2、變量不需要和其他變量共同參與不變約束;

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

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

3、volatile特性在虛擬機層面的實現

Java volatile 從入門到精通

圖1

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

4、volatile示例代碼

4.1、是否使用volatile修飾的共享變量

如下面圖2、圖3所示:

Java volatile 從入門到精通

圖2


Java volatile 從入門到精通

圖3

上面的圖2、圖3的代碼,在服務端模式下運行時,會在打印出"下面終止運行"後也一直打印"運行中..."。

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

當如下面圖4中所示,加了紅線上放的volatile後,則會正常的終止打印了。

Java volatile 從入門到精通

圖4

4.1、使用volatile修飾的共享的變量自增

如下面圖5、圖6所示:

Java volatile 從入門到精通

圖5


Java volatile 從入門到精通

圖6

打印結果如下面圖7所示:可見並不能正常的進行自增操作,按我們的預期count值應該是100*100,但實際結果卻並非如我們預期。

Java volatile 從入門到精通

圖7

持續更新完善中......歡迎關注,共同學習


分享到:


相關文章: