JAVA内存不可见问题

线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时,导致出现脏数据或者其他不可预见的结果的问题。
上面这句话有两个比较重要的地方,也是本文着重归纳的地方,一个是共享变量,一个是同步措施。
要理解线程安全问题,首先得理解共享变量的内存模型。

内存模型

所有的变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的主内存副本拷贝。线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成。

Java内存模型规定,将所有的变量都存放在主内存中,当线程使用变量时,会把主内存里面的变量复制到自己的工作空间或者叫作工作内存,线程读写变量时操作的是自己工作内存中的变量。 Java内存模型是一个抽象的概念,在实际实现中线程的工作内存如下图所示

为什么会有一级缓存二级缓存的出现,这是因为处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致。
举个列子说明这个问题:
1.线程A首先获取共享变量X的值,由于两级Cache 都没有命中所以加载主内存中X的值,假如为0。然后把X=O的值缓存到两级缓存,线程A修改X的值为1,
然后将其写入两级Cache,并且刷新到主内存。线程A操作完毕后,线程A所在的CPU的两级Cach内和主内存里面的X的值都是1。
2.线程B获取X的值首先一级缓存没有命中,然后看二级缓存,二级缓存命中了,所以返回 X=1;到这里一切都是正常的,因为这时候主内存中也是X=1。然后线程B修改X的值为2,并将其存放到线程2所在的一级Cache和共享二级 Cache中,最后更新主内存中 X 的值为 2 : 到这里一切都是好的 。
3.线程A这次又需要修改X的值,获取时一级缓存命中,并且 X=1,到这里问题就出现了,明明线程B已经把X的值修改为了2 ,为何线程A获取的还是l呢?这就是共享变量的内存不可见问题 ,也就是线程B写入的值对线程A不可见。

同步方法

可以采用volatile关键字来解决内存不可见问题。
volatile的内存语义为:当线程写入了volatile变量值时就把写入工作内存的变量值同步到主内,读取volatile变量值时就先清空本地内存变量值,再从主内存获取最新值。

参考资料《JAVA并发编程之美》

猜你喜欢

转载自www.cnblogs.com/cmg219/p/12303758.html