1. 多线程操作共享变量引发问题
我们通过下面代码来分析下内存可见性问题:
package com.pyy.juc;
public class TestVolatile {
public static void main(String[] args) {
// 这个线程为flag 修改值
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
// 主线程无线循环判断这个flag值
while(true) {
if(td.isFlag()) {
System.out.println("============");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
}
运行代码,会发现控制台输出:flag=true,但程序(主线程)并没有结束。这里就涉及到多线程的内存可见性
问题:
出现上述问题的原因,主要是因为多个线程操作共享数据
时彼此不可见
。
解决内存可见性问题可以使用同步锁(synchronized)
while(true) {
synchronized(td) {
if(td.isFlag()) {
System.out.println("============");
break;
}
}
}
通过同步锁方式(可以保证每次都刷新主存,保证共享数据的同步性),但只要用到锁就会引发线程等待,影响代码效率。
2. volatile关键字
这里就引出了一个新的关键字volatile
,它就可以保证多个线程操作共享数据内存中的数据是彼此可见的。底层是通过内存屏障
,我们可以理解volatile修饰的变量是在主存中的数据
直接操作。
读写变量都在 主存
中直接操作,进而保证多线程可见性。而且效率上要比同步锁高的多。
其实有说volatile效率底,是因为jvm底层有个指令排序,但使用volatile修饰的变量会有一个禁止指令重排
限制
3. 代码重构后
...
// 使用volatile修饰变量
private volatile boolean flag = false;
...
就可以解决多个线程操作共享数据彼此不可见问题
4. volatile和synchronized区别
volatile只是一个相比synchronized来说较为轻量级
的同步策略
- volatile 不具备
互斥性
- volatile 不能保证修饰变量的
原子性
后面我们在谈论下有关变量原子性和CAS算法问题