一,为什么有线程可见性的问题
先看看下面这段简单的代码:
public class VolatileTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
while (true) {
if (myThread.flag) {
System.out.println("====================================");
}
}
}
static class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(100);
flag = true;
System.out.println("flag="+flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean flag = false;
public boolean isFlag() {
return flag;
}
}
}
按道理讲,在线程myThread中已经将flag置为true了,为什么main方法里面的print方法没有执行呢?
就是说子线程的flag值对于main线程是不可见的,这就是线程可见性。
二,线程可见性的根本原因
线程可见性的根本原因在于jvm的内存结构:
- 共享变量必须存放在主内存中
- 线程有自己的工作内存,线程只可以操作自己的工作内存
- 线程要操作共享变量,需要从主内存中读取到工作内存,改变值后需要从工作内存同步到主内存中
在上面的例子中,flag是共享变量,main方法从主内存中读取flag,而线程myThread从主内存将flag读取到工作内存中,并在工作内存中修改了flag的值,但并没有及时的将flag的值同步回主内存。
三,解决办法
-
使用volatile关键字
volatile语义规范:- 使用volatile变量时,必须从主内存中加载,并且read、load是连续的
- 修改volatile变量后,必须立马同步回主内存,并且store、write是连续的
vilatile关键字的作用:
- 确保线程可见性
- 禁止指令重排
-
将修改flag的代码用synchronized关键字包裹
或者