volatile關鍵字與內存可見性


前言

首先,我們使用多線程的目的在於提高程序的效率,但是如果使用不當,不僅不能提高效率,反而會使程序的性能更低,因為多線程涉及到線程之間的調度、CPU上下文的切換以及包括線程的創建、銷毀和同步等等,開銷比單線程大,因此需謹慎使用多線程。

在jdk1.5以后,提供了一個強大的java.util.concurrent包,這個包中提供了大量的應用於線程的工具類。

下面開始介紹volatile關鍵字和內存可見性,雖然volatile是在jdk1.5之前就有的,但還是想放在這里講一下。

舉例說明

首先,我們先看一段小程序。

 1 package com.ccfdod.juc;
 2 
 3 public class TestVolatile {
 4     public static void main(String[] args) {
 5         ThreadDemo td = new ThreadDemo();
 6         new Thread(td).start();
 7         
 8         while(true) {
 9             if (td.isFlag()) {
10                 System.out.println("--------------");
11                 break;
12             }
13         }
14     }
15 }
16 
17 class ThreadDemo implements Runnable {
18     private boolean flag = false;
19 
20     @Override
21     public void run() {
22         try {
23             Thread.sleep(200);
24         } catch (InterruptedException e) {
25             e.printStackTrace();
26         }
27         flag = true;
28         System.out.println("flag = " + isFlag());
29     }
30 
31     public boolean isFlag() {
32         return flag;
33     }
34 
35     public void setFlag(boolean flag) {
36         this.flag = flag;
37     }
38 }

程序運行結果:

flag = true

並且程序不會停止。

按理來說,應該會在td線程修改flag值后,主線程會打印出“--------------”,但是為什么沒有出現預期效果呢?下面來分析這段程序,涉及到內存可見性問題。

內存可見性問題

當程序運行時,JVM會為每一個執行任務的線程分配一個獨立的緩存空間,用於提高效率。

不難理解,程序開始執行時,由於線程td修改flag操作之前,sleep了200ms,td線程和main線程獲取到的flag都為false,但為什么td線程將flag改為true后,main線程沒有打印出“--------------”呢?原因在於:while(true)是執行效率很高,使得main線程沒有時間再次從主存中獲取flag的值,因此程序在td線程將flag修改為true后,沒有停止運行的原因。其實在while(true)后面稍微延遲一點(比如說,打印一句話),都會使main線程將主存中的flag=true讀取。

產生這種情況的原因就在於,兩個線程在操作共享數據時,對共享數據的操作是彼此不可見的。

那么為了不讓這種問題出現,怎么解決呢?

一、使用synchronized同步鎖

while(true) {
    synchronized (td) {
        if (td.isFlag()) {
            System.out.println("--------------");
            break;        
        }
    }
}

使用synchronized同步鎖能保證數據的及時更新。但是效率太低。

二、使用volatile關鍵字

當多個線程進行操作共享數據時,可以保證內存中的數據可見。底層原理:內存柵欄。使用volatile關鍵字修飾時,可理解為對數據的操作都在主存中進行。

private volatile boolean flag = false;

相較於synchronized是一種較為輕量級的同步策略。

注意:

  • volatile不具備“互斥性”
  • volatile不能保證變量的“原子性”

關於“原子性”的問題將在下一篇博客中討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM