在Java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,觉得使用这个关键字,在进行多线程并发处理的时候就能够万事大吉。html
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块(synchronized) 和 volatile 关键字机制。java
synchronized(不作过多解释)多线程
同步块你们都比较熟悉,经过 synchronized 关键字来实现,全部加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程可以用并发
synchronized 修饰的方法 或者 代码块。jvm
volatile性能
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操做。测试
若是要深刻了解volatile关键字的做用,就必须先来了解一下JVM在运行时候的内存分配过程。优化
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每个线程运行时都有一个线程栈,spa
线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先经过对象的引用找到对应在堆内存的变量的值,而后把堆内存线程
变量的具体值load到线程本地内存中,创建一个变量副本,以后线程就再也不和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,
在修改完以后的某一个时刻(线程退出以前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图
描述这写交互!
那么在了解完JVM在运行时候的内存分配过程之后,咱们开始真正深刻的讨论volatile的具体做用
请看代码:
1 public class VolatileTest extends Thread { 2 3 boolean flag = false; 4 int i = 0; 5 6 public void run() { 7 while (!flag) { 8 i++; 9 } 10 } 11 12 public static void main(String[] args) throws Exception { 13 VolatileTest vt = new VolatileTest(); 14 vt.start(); 15 Thread.sleep(2000); 16 vt.flag = true; 17 System.out.println("stope" + vt.i); 18 } 19 }
上面的代码是经过标记flag来控制VolatileTest线程while循环退出的例子!
下面让我用伪代码来描述一下咱们的程序
上面的叙述看似并无什么问题,“彷佛”彻底正确。那就让咱们把程序运行起来看看效果吧,执行mian方法。2秒钟之后控制台打印stope-202753974。
但是奇怪的事情发生了 程序并无退出。vt线程仍然在运行,也就是说咱们在主线程设置的 vt.flag = true;没有起做用。
那么按照咱们上面所讲的 “JVM在运行时候的内存分配过程” 就很好解释上面的问题了。
首先 vt线程在运行的时候会把 变量 flag 与 i (代码3,4行)从“主内存” 拷贝到 线程栈内存(上图的线程工做内存)
而后 vt线程开始执行while循环
7 while (!flag) { 8 i++; 9 }
while (!flag)进行判断的flag 是在线程工做内存当中获取,而不是从 “主内存”中获取。
i++; 将线程内存中的i++; 加完之后将结果写回至 "主内存",如此重复。
而后再说说主线程的执行过程。 我只说明关键的地方
vt.flag = true;
主线程将vt.flag的值一样 从主内存中拷贝到本身的线程工做内存 而后修改flag=true. 而后再将新值回到主内存。
这就解释了为何在主线程(main)中设置了vt.flag = true; 而vt线程在进行判断flag的时候拿到的仍然是false。那就是由于vt线程每次判断flag标记的时候是从它本身的“工做内存中”取值,而并不是从主内存中取值!
这也是JVM为了提供性能而作的优化。那咱们如何能让vt线程每次判断flag的时候都强制它去主内存中取值呢。这就是volatile关键字的做用。
再次修改咱们的代码
public class VolatileTest extends Thread { volatile boolean flag = false; int i = 0; public void run() { while (!flag) { i++; } } public static void main(String[] args) throws Exception { VolatileTest vt = new VolatileTest(); vt.start(); Thread.sleep(2000); vt.flag = true; System.out.println("stope" + vt.i); } }
在flag前面加上volatile关键字,强制线程每次读取该值的时候都去“主内存”中取值。在试试咱们的程序吧,已经正常退出了。
https://www.cnblogs.com/daxin/p/3364014.html