前言
不管是在面试还是实际开发中 volatile 都是一个应该掌握的技能。
首先来看看为什么会出现这个关键字。
内存可见性
由于 Java 内存模型(JMM)规定,所有的变量都存放在主内存中,而每个线程都有着自己的工作内存(高速缓存)。
线程在工作时,需要将主内存中的数据拷贝到工作内存中。这样对数据的任何操作都是基于工作内存(效率提高),并且不能直接操作主内存以及其他线程工作内存中的数据,之后再将更新之后的数据刷新到主内存中。
这里所提到的主内存可以简单认为是堆内存,而工作内存则可以认为是栈内存。
如下图所示:
所以在并发运行时可能会出现线程 B 所读取到的数据是线程 A 更新之前的数据。
显然这肯定是会出问题的,因此 volatile 的作用出现了:
当一个变量被 volatile 修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。
volatile 修饰之后并不是让线程直接从主内存中获取数据,依然需要将变量拷贝到工作内存中。
内存可见性的应用
当我们需要在两个线程间依据主内存通信时,通信的那个变量就必须的用 volatile 来修饰:
1 public class Volatile implements Runnable{ 2 3 private static volatile boolean flag = true ; 4 5 @Override 6 public void run() { 7 while (flag){ 8 } 9 System.out.println(Thread.currentThread().getName() +"执行完毕"); 10 } 11 12 public static void main(String[] args) throws InterruptedException { 13 Volatile aVolatile = new Volatile(); 14 new Thread(aVolatile,"thread A").start(); 15 16 17 System.out.println("main 线程正在运行") ; 18 19 Scanner sc = new Scanner(System.in); 20 while(sc.hasNext()){ 21 String value = sc.next(); 22 if(value.equals("1")){ 23 24 new Thread(new Runnable() { 25 @Override 26 public void run() { 27 aVolatile.stopThread(); 28 } 29 }).start(); 30 31 break ; 32 } 33 } 34 35 System.out.println("主线程退出了!"); 36 37 } 38 39 private void stopThread(){ 40 flag = false ; 41 } 42 43 }