5.9 Volatile关键字
5.9.1 并发变量下变量不可见性问题
引入:在多个线程访问共享变量过程中, 一个线程修改变量的值后,其他线程看不到变量最新值的情况。
1、VolatileDemo01线程在run方法中修改flag变量的值为true
@Override
public void run() {
flag = true;
System.out.println(Thread.currentThread().getName()+":"+"flag = " + flag);
}
2、在Test类中启动VolatileDemo01线程并且在Test主线程中监视flag变量
while (true){
if(t.isFlag()){
System.out.println("主线程进入执行");
}
}
3、运行结果:VolatileDemo01线程将flag修改之后,Test主线程并没有监控到flag有变化,这就是并发变量下变量不可见性问题
5.9.2 变量不可见性的内存语义
在介绍多线程并发修改变量不可见现象的问题原因之前,我们需要了解一下Java内存模型(java并发编程有关的模型):JMM
JMM(Java Memory Model);Java内存模型,是Java虚拟机规范中所定义的一种内存,Java内存模型是标准化的,屏蔽掉了底层不同计算机的区别。
Java内存模型描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节
JMM有一下规定:
- 所有的共享变量都存储于主内存,这里所说 的变量指的是实例变量和类变量,不包括局部变量,因为局部变量是线程私有的,不存在竞争问题。
- 每一个线程还存在自己的工作内存,线程的工作内存保留了被线程使用的变量的工作副本
- 线程对变量的读写操作都是在工作内存中完成,而不是直接读写与主内存中的变量。
- 不同线程之间不能直接访问对方工作内存中的变量,线程之间的变量传递需要通过主内存中转来完成。
5.9.3 解决方案(并发变量下变量不可见性问题)
① synchronized
加锁实现重新将主内存的共享变量拷贝至子线程的工作内存。
while (true){
synchronized (VolatileDemo01.class){
if(t.isFlag()){
System.out.println("主线程进入执行");
}
}
}
② 给共享变量加上volatile关键字
private volatile boolean flag;
5.9.4 volatile
与synchronized
volatile
只能修饰实例变量和类变量,而synchronized
可以修饰方法以及代码块volatile
保证数据的可见性问题,但是不保证原子性(多线程进行缬草组,不保证线程安全)而synchronized
是一种排他(互斥)的机制。即volatile
不保证线程安全,而synchronized
是线程安全的
5.9.5 volitile 关键字
使用volatile
修饰的变量的值在被一个线程修后,其他线程可以获取到这个共享变量的最新值
使用volatile修饰共享变量,就可以解决并发变量下变量不可见性问题
完整代码如下:
public class VolatileDemo01 extends Thread{
// private volatile boolean flag;
private boolean flag;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
flag = true;
System.out.println(Thread.currentThread().getName()+":"+"flag = " + flag);
}
public VolatileDemo01(boolean flag, String name) {
super(name);
this.flag = flag;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
class Test {
public static void main(String[] args) {
// 启动子线程
VolatileDemo01 t = new VolatileDemo01(false, "子线程");
t.start();
// 主线程
while (true){
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"flag = "+t.isFlag());
// 这里需要使用到VolatileDemo01中的变量
synchronized (VolatileDemo01.class){
if(t.isFlag()){
System.out.println("-------------------------");
System.out.println("主线程进入执行");
System.out.println(Thread.currentThread().getName()+":"+"flag = "+t.isFlag());
System.out.println("-------------------------");
}
}
}
}
}