java volatile關鍵字做用及使用場景

 

1. volatile關鍵字的做用:保證了變量的可見性(visibility)。被volatile關鍵字修飾的變量,若是值發生了變動,其餘線程立馬可見,避免出現髒讀的現象。如如下代碼片斷,isShutDown被置爲true後,doWork方法仍有執行。如用volatile修飾isShutDown變量,可避免此問題。多線程

 1 public class VolatileTest3 {
 2     static class Work {
 3         boolean isShutDown = false;
 4 
 5         void shutdown() {
 6             isShutDown = true;
 7             System.out.println("shutdown!");
 8         }
 9 
10         void doWork() {
11             while (!isShutDown) {
12                 System.out.println("doWork");
13             }
14         }
15     }
16 
17     public static void main(String[] args) {
18         Work work = new Work();
19 
20         new Thread(work::doWork).start();
21         new Thread(work::doWork).start();
22         new Thread(work::doWork).start();
23         new Thread(work::shutdown).start();
24         new Thread(work::doWork).start();
25         new Thread(work::doWork).start();
26         new Thread(work::doWork).start();
27     }
28 }

出現髒讀時,運行結果以下:app

2. 爲何會出現髒讀?spa

Java內存模型規定全部的變量都是存在主存當中,每一個線程都有本身的工做內存。線程對變量的全部操做都必須在工做內存中進行,而不能直接對主存進行操做。而且每一個線程不能訪問其餘線程的工做內存。變量的值什麼時候從線程的工做內存寫回主存,沒法肯定。.net

3. happens-before規則的理解與勘誤線程

在網上查volatile關鍵字相關信息時,多篇博客提到了happens-before原則,我的對此原則的理解是:當操做該volatile變量時,全部前序對該變量的操做都已完成(如不存在已變動,但未寫回主存的狀況),全部後續對該變量的操做,都未開始。僅此而已。code

這裏,我認爲網上很常見的一個理論對此理解有誤,以下圖。此觀點認爲,因爲volatile變量flag的happens-before原則,因此A線程2處對其的寫操做必定先於B線程3處對其的讀操做。其實這種觀點是有邏輯缺陷的,若是存在一個C線程,先讀取flag的值,後寫入flag的值,那C線程的執行時機是什麼呢?若是還有其餘D、E線程呢。。。對於這段代碼的正確理解是,只要3處拿到的flag是true,那麼a的值必定是1,而不是0.由於volition修飾的變量,處理器不會對其進行重排序,因此1處對a的賦值,必定發生在2處對flag的賦值以前。若是flag不是volatile變量,那麼1處和2處代碼的執行順序是沒法保證的(處理器的指令重排序),雖然大部分狀況1會先於2執行。happens-before原則約束的並非多線程對同一變量的讀和寫操做之間的順序,而是保證讀操做時,前序全部對該變量的寫操做已生效(寫回主存)。blog

 

 

驗證以下:排序

 1 public class VolatileTest {
 2     static class A {
 3         int a = 0;
 4         volatile boolean flag = false;
 5 
 6         void writer() {
 7             a = 1;                   //1
 8             flag = true;               //2
 9             System.out.println("write");
10         }
11 
12         void reader() {
13             if (flag) {                //3
14                 int i =  a;           //4
15                 System.out.println("read true");
16                 System.out.println("i is :" + i);
17             } else {
18                 int i = a;
19                 System.out.println("read false");
20                 System.out.println("i is :" + i);
21             }
22         }
23 
24     }
25 
26     public static void main(String[] args) {
27         A aaa = new A();
28         new Thread(() -> aaa.reader()).start();
29         new Thread(() -> aaa.writer()).start();
30     }
31 }

運行結果以下,在寫操做執行以前,讀操做已完成內存

 

 4. volatile關鍵字使用場景get

注意:volatile只能保證變量的可見性,不能保證對volatile變量操做的原子性,見以下代碼:

 

 1 public class VolatileTest2 {
 2     static class A {
 3         volatile int a = 0;
 4         void increase() {
 5             a++;
 6         }
 7         int getA(){
 8             return a;
 9         }
10     }
11 
12     public static void main(String[] args) {
13         A a = new A();
14 
15         new Thread(() -> {
16             for (int i = 0;i < 1000;i++) {
17                 a.increase();
18             }
19             System.out.println(a.getA());
20         }).start();
21         new Thread(() -> {
22             for (int i = 0;i < 2000;i++) {
23                 a.increase();
24             }
25             System.out.println(a.getA());
26         }).start();
27         new Thread(() -> {
28             for (int i = 0;i < 3000;i++) {
29                 a.increase();
30             }
31             System.out.println(a.getA());
32         }).start();
33         new Thread(() -> {
34             for (int i = 0;i < 4000;i++) {
35                 a.increase();
36             }
37             System.out.println(a.getA());
38         }).start();
39         new Thread(() -> {
40             for (int i = 0;i < 5000;i++) {
41                 a.increase();
42             }
43             System.out.println(a.getA());
44         }).start();
45     }
46 }

 

運行結果以下,volatile沒法保證a++操做的原子性。

volatile正確的使用方法可參考:https://blog.csdn.net/vking_wang/article/details/9982709

相關文章
相關標籤/搜索