线程学习之后,大概了解了线程的功能和原理,这里不再赘述。
什么是内存可见性呢?
当若干个线程共享同一个资源的时候,这个资源就处于临界状态,如果处理不当很容易发生一些不愿意看到的错误。
举个栗子:
- 创建一个ThreadDemo类实现Runnable接口
- 有一个boolean的变量flg默认值是false
- 在run()方法中改变flg的值为true
- 然后在main方法中创建一个线程执行start方法
- 在线程中判断flg的值是否是true,并打印语句
class ThreadDemo implements Runnable{
private boolean flg = false;
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.flg = true;
System.out.println("ThreadDemo.run()->flg:"+this.flg);
}
public boolean isFlg() {
return flg;
}
public void setFlg(boolean flg) {
this.flg = flg;
}
}
TestVolatile类和main方法
public class TestVotile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while(true){
if(td.isFlg()){
System.out.println("CurrentThread->");
break;
}
}
}
}
执行main方法之后观察测试结果:
结果令人感到意外?而且发现eclipse上的红色按钮表示程序并没有结束而是一直再循环。
原因分析
在多线程中JVM虚拟机为了优化和提高效率,为每一个线程都提供了一个单独的缓存空间。这样在这个例子中共享变量flg就产生了三个空间,一个是主存,在堆中,另外两个一个在线程td的缓存中,另外一个在main线程的缓存中。
如图所示:
在td线程拿到flg之后放在自己的缓存中,并且修改flg为true然后把修改后的值刷新到主内存中,但是由于main线程的while循环效率非常高,在主内存的值被刷新之前就获得到了flg的值,为false,所以不会执行打印语句,即便是主存中的flg值已经被刷新了,但是由于while的影响,没机会在主内存中多的最新的值,导致flg在main的while中一直是false,一直处于循环状态什么都不做。而且主线程也不会结束。
volatile关键字
对于上属于的问题就是内存可见性的问题,在TreadDemo类想flg变量之前加上volatile修饰符,使得双方的操作的共享资源可以及时的得到更新,甚至可以看成双方对共享资源的操作就是指主存中进行的,而且共享资源的的改变可以被任何一个线程得到,这样就保证了内存的可见性。
class ThreadDemo implements Runnable{
private volatile boolean flg = false;
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.flg = true;
System.out.println("ThreadDemo.run()->flg:"+this.flg);
}
public boolean isFlg() {
return flg;
}
public void setFlg(boolean flg) {
this.flg = flg;
}
}
再次测试输出结果为
此时线程结束,主线程也打印了语句。