关于线程的可见性
什么是线程间的可见性?
一个线程对共享变量值的修改,能够及时的被其他线程看到。
什么是共享变量?
如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。
什么是java内存模型?(Java Memory Model,简称JMM)
JMM描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。
- 规则1:
1>所有的变量都存储在主内存中
2>每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)- 规则2:
1>线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
2>不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量的传递需要通过主内存来完成
示例代码
public class VisableDemo {
//volatile 解决可见性问题
public volatile static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
int i = 0;
while (!stop){
i++;
}
System.out.println("result:"+i);
});
thread.start();
System.out.println("begin start thread");
Thread.sleep(10000);
stop = true;
}
}
Lock指令的作用:
- 将当前处理器缓存行的数据写回到系统内存
- 这个写回内存的操作会使在其他cpu里缓存了该内存地址的数据无效
什么时候需要用volatile?
当存在多个线程对同一个共享变量进行修改的时候,需要增加volatile,保证数据修改的实时可见。
指令重排序会导致运行顺序发生变化,如下图:
这两个方法运行顺序有可能不是顺序进行的,有可能会发生exeToCPU1()先执行,造成了value的值计算出现差错。
为了避免这种问题出现,cup层面做了处理:
- Store Barrier :强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存
- Load Barrier :强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令
- Full Barrier :复合了load和store屏障的功能
内存屏障