不可见性Volatile
引入:
多个线程访问共享变量,会出现一个线程修改变量的值后,其他线程看不到变量最新值的情况。
结论:
并发编程下,多线程修改变量。公出现战程间变量的不可见性。
不可见性的原因:
每个线程都有自己的工作内存,线程都是从主内存拷贝共享变量的副本值。
每个线程是在自己的工作内存中操作共享变量的。
例如:
下列例子中,flag子线程已经更改为ture,然而却不能进入主线程。是因为每个线程都有自己的工作内存,子线程更改,提交给主内存,但主线程没有从主内存得到新值来更新自己的工作内存
public class Demo1{
public static void main(String[] args) {
//子线程启动
vision t1 = new vision();
t1.start();
//主线程
while(true){
if (t1.isFlag()){
System.out.println("主线程进入执行");
}
}
}
}
class vision extends Thread{
private boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try {
Thread.sleep(10000);
}catch (Exception e){
e.printStackTrace();
}
flag = true;
System.out.println(flag);
}
}
不可见性的解决 :
解决方案有两种常见方式:
- 加锁
- 对共享的变量进行volatile关键字修饰。
一、加锁
某一个线程进入synchronized代码块前后,执行过程入如下:
- a. 线程获得锁
- b. 清空工作内存
- c. 从主内存拷贝共享变量最新的值到工作内存成为副本
- d. 执行代码
- e. 将修改后的副本的值刷新回主内存中
- f. 线程释放锁
//子线程启动
vision2 t1 = new vision2();
t1.start();
//主线程
while(true){
//加锁会清空工作内存,读取主内存最新值到工作内存来
synchronized(vision2.class){
if (t1.isFlag()){
System.out.println("主线程进入执行");
}
}
}
二、volatile修饰
直接给变量加上volatile修饰
class vision2 extends Thread{
private volatile boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try {
Thread.sleep(10000);
}catch (Exception e){
e.printStackTrace();
}
flag = true;
System.out.println(flag);
}
}
- 子线程从主内存读取到数据放入其对应的工作内存
- 将flag的值更改为true,但是这个时候flag的值还没有写到主内存
- 此时main方法main方法读取到了flag的值为false
- 当子线程将flag的值写回去后,失效其他线程对此变量副本
- 再次对flag进行操作的时候线程会从主内存读取最新的值,放入到工作内存中
总结:
-
volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量, 当修改写回主内存时,另外一个线程立即看到最新的值。
-
加锁会清空线程自己的工作内存,重新读取主内存的最新值。