前言
很多同学都知道,volatile关键字是来保证内存可见性的。那如果变量没有被volatile修饰,它一定是不可见的么?我们来一探究竟。
示例
public class MyVolatile {
private boolean flag = true;
public boolean getFlag() {
return this.flag;
}
public void stopThread() {
System.out.println(Thread.currentThread().getName() +
":stopThread");
flag = false;
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyVolatile myVolatile = new MyVolatile();
new Thread(() -> {
//或者for(;;) {}
while(true) {
if(!myVolatile.getFlag()) {
System.out.println(Thread.currentThread().getName() +
"执行完毕");
break;
}
}
}).start();
Thread.sleep(500);
myVolatile.stopThread();
}
}
看下结果:
程序没有终止:说明子线程拿到的myVolatile对象的flag属性,一直为true。此时flag属性是内存不可见的。
我们把子线程的执行代码修改一下,其余不变:
new Thread(() -> {
while(myVolatile.getFlag()) {
System.out.println(Thread.currentThread().getName() +
"正在运行。。。");
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}).start();
运行结果:
这里又行了,flag属性又可见了!!!那这是为什么呢?笔者也是查阅了相关资料,第一种while(true)的写法,如果循环体的执行效率非常高的话,执行时就不会再去获取flag属性的最新值了,即不可见了;而第二种写法,在while中加入了判断:flag属性是否为true,所以每次循环前都要去获取flag属性的最新值,此时就是可见的。
现在把第一种写法的循环体改动一下,不要让它的执行效率那么高:
new Thread(() -> {
//或者for(;;) {}
while(true) {
if(!myVolatile.getFlag()) {
System.out.println(Thread.currentThread().getName() +
"执行完毕");
break;
}
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
或者使用同步方法:
Object o = new Object();
new Thread(() -> {
//或者for(;;) {}
while(true) {
synchronized (o) {
if (!myVolatile.getFlag()) {
System.out.println(Thread.currentThread().getName() +
"执行完毕");
break;
}
}
}
}).start();
再看运行结果:
此时flag属性是可见的了。但这2种是不是性能就低下了,这时volatile关键字就派上用场了,把它加到flag属性上:
private volatile boolean flag = true;
子线程再用原先的第一种写法,运行后程序是能终止的。