Java關鍵字volatile的理解
一.導讀
在《Java記憶體模型的理解》一文中,我們提到了volatile關鍵字可以保證可見性,今天我們來聊聊這個volatile關鍵字。
二.volatile深入解析
其實對記憶體模型有了一定的瞭解後,我們對volatile的理解就容易多了,volatile可以實現可見性、有序性,但是無法實現原子性。volatile的登場就是想解決在併發訪問中,讀取和更新變數的時候,要直接對主記憶體進行操作,而不是先操作自己的工作記憶體,然後在更新主記憶體這樣的流程,用volatile修飾的變數會強制將assign賦值操作和store、write操作繫結在一起,將use使用操作強制和read、load操作繫結在一起,這樣assign之後必須執行store、write操作,use操作之前必須先執行read、load操作。
1.可見性
volatile之所以能做到從主存中讀寫資料,是因為在併發過程中一個執行緒對volatile變數進行了修改操作後,會先寫到工作記憶體,通過《Java記憶體模型的理解》中的硬體記憶體架構與Java記憶體模型關係圖中,我們瞭解到,底層其實就是儲存到CPU快取記憶體中,這樣會觸發一個LOCK指令,這個指令會進行如下操作:
(1).鎖定匯流排或者快取,將修改後的新值儲存到記憶體RAM中,也就是JVM關係圖中對應的主記憶體中。
(2).會將其他CPU的快取記憶體中的這個變數的值設定為無效,也就是JVM關係圖中對應的工作記憶體裡儲存的這個變數值設定為無效。
綜上兩個操作,其他執行緒想要對變數進行操作時,讀取變數時發現自己工作記憶體中的值是無效的,就從主記憶體重新讀取,並儲存到工作記憶體,這樣就達到了可見性。
2.原子性
volatile對變數的操作是不具有原子性的,這裡有一個經典的例子。
(1).非原子性
package cn.xiangquba;
public class volatileDemo {
public volatile int x = 0;
public void create() {
x++;
}
public static void main(String[] args) {
volatileDemo instance = new volatileDemo();
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 10; j++) {
instance.create();
}
};
}.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(instance.x);
}
}
(2).AtomicInteger實現原子性
package cn.xiangquba;
import java.util.concurrent.atomic.AtomicInteger;
public class volatileDemo {
AtomicInteger x = new AtomicInteger();
public void create() {
x.incrementAndGet();
}
public static void main(String[] args) {
volatileDemo instance = new volatileDemo();
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 10; j++) {
instance.create();
}
};
}.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(instance.x);
}
}
(3).Lock實現原子性
package cn.xiangquba;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class volatileDemo {
public int x = 0;
Lock lock = new ReentrantLock();
public void create() {
lock.lock();
try {
x++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
volatileDemo instance = new volatileDemo();
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 10; j++) {
instance.create();
}
};
}.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(instance.x);
}
}
(4).synchronized實現原子性
package cn.xiangquba;
public class volatileDemo {
public int x = 0;
public synchronized void create() {
x++;
}
public static void main(String[] args) {
volatileDemo instance = new volatileDemo();
for (int i = 0; i < 5; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 10; j++) {
instance.create();
}
};
}.start();
}
while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(instance.x);
}
}
3.有序性
再講記憶體屏障的時候,提到了volatile底層是通過記憶體屏障的方式來禁止重排序,有三句很繞的話:
(1).當第二個操作為volatile寫操做時,不管第一個操作是什麼(普通讀寫或者volatile讀寫),都不能進行重排序。這個規則確保volatile寫之前的所有操作都不會被重排序到volatile之後;
(2).當第一個操作為volatile讀操作時,不管第二個操作是什麼,都不能進行重排序。這個規則確保volatile讀之後的所有操作都不會被重排序到volatile之前;
(3).當第一個操作是volatile寫操作時,第二個操作是volatile讀操作,不能進行重排序。
除以上三種情況以外可以進行重排序。另:以上三句話摘自《深入理解Java記憶體模型》
簡單舉個例子說明一下:
int x = 1; //語句1
int y = 2; //語句2
volatile boolean bflag = false; //語句3
int m = 3; //語句4
int n = 4; //語句5
因為我們的變數bflag是用關鍵字volatile修飾,指令重排序的時候,語句1和語句2之間的順序是無法保證的,同樣語句4和語句5的順序也是無法保證的,但是語句1和語句2一定在語句3前面,語句4和語句5一定在語句3後面。並且語句3執行的時候,語句1和語句2一定執行完畢。
三.參考文獻
1.深入理解Java虛擬機器
2.揭祕Java虛擬機器-JVM設計原理與實現
3.深入理解Java記憶體模型
個人部落格原文:https://www.xiangquba.cn/2018/03/03/java-volatile/
相關文章
- 深入理解Java中的volatile關鍵字Java
- 快速理解 volatile 關鍵字
- 深入彙編指令理解Java關鍵字volatileJava
- 兩張圖理解volatile關鍵字
- Java volatile關鍵字作用Java
- Java volatile關鍵字解析Java
- Java併發專題(三)深入理解volatile關鍵字Java
- 全面理解Java記憶體模型(JMM)及volatile關鍵字Java記憶體模型
- 深入瞭解 Java 的 volatile 關鍵字Java
- Volatile關鍵字
- java併發之volatile關鍵字Java
- Java併發—— 關鍵字volatile解析Java
- 深入理解Java記憶體模型JMM與volatile關鍵字Java記憶體模型
- Java記憶體模型——volatile關鍵字Java記憶體模型
- JAVA static關鍵字理解Java
- volatile關鍵字解析
- Java面試官最愛問的volatile關鍵字Java面試
- volatile關鍵字的作用、原理
- Java面試題:請談談Java中的volatile關鍵字?Java面試題
- Java併發程式設計volatile關鍵字Java程式設計
- Java多執行緒(二)volatile關鍵字Java執行緒
- java併發程式設計——volatile關鍵字Java程式設計
- java記憶體模型及volatile關鍵字Java記憶體模型
- java併發程式設計:volatile關鍵字Java程式設計
- Java面試題集錦(1):volatile關鍵字Java面試題
- java多執行緒4:volatile關鍵字Java執行緒
- volatile關鍵字解析~高階java必問Java
- Java記憶體模型與volatile關鍵字Java記憶體模型
- 深入解析volatile關鍵字
- Java 面試官最喜歡問的關鍵字 volatileJava面試
- 從根源上解析 Java volatile 關鍵字的實現Java
- Java併發程式設計:volatile關鍵字解析Java程式設計
- Java執行緒面試題(03) Java中的volatile如何工作? Java中的volatile關鍵字示例Java執行緒面試題
- [C#.NET 拾遺補漏]10:理解 volatile 關鍵字C#
- 一個具體的例子學習Java volatile關鍵字Java
- 深入理解Java多執行緒與併發框(第⑦篇)——volatile 關鍵字Java執行緒
- C語言中volatile關鍵字的作用C語言
- 面試官最愛的volatile關鍵字面試