java的内存模型
对于可见性,Java 提供了 volatile 关键字来保证可见性、有序性。但不保证原子性。 普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
- volatile 关键字对于基本类型的修改可以在随后对多个线程的读保持一致,但是对于引用类型如数组,实体 bean,仅仅保证引用的可见性,但并不保证引用内容的可见性。。
- 禁止进行指令重排序。
背景:为了提高 CPU 处理速度,使用高速缓存解决了 CPU 和主存速率不匹配的问题,处理器(CPU)不直接和内存进行通信,而是先将系统内存的数据读到内部高速缓存(寄存器、L1,L2 等)后再进行操作,但同时又引入另外一个新问题:缓存一致性问题。
# 硬件内存架构
主内存:主存
工作内存:可以直接操作的(寄存器,L1、L2 缓存等)。各线程能够独有的,只能访问自己的工作内存
Java 中普通的共享变量不保证可见性,因为数据修改被写入主内存的时机是不确定的,多线程并发下很可能出现"脏读",所以每个线程都有自己的工作内存,线程自己的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,赋值等 )都必需在线程自己的工作内存中进行,而不能够直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
比如线程 1 修改的变量 A,并不会立即同步会主内存,这时线程 2 使用变量 B 就会有问题
关键技术点都是围绕多线程(并发)的原子性、可见性和有序性展开的。
如果对声明了 volatile 的变量进行写操作,JVM 就会向处理器发送一条指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。
在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据。
# 总结下来:
第一:使用 volatile 关键字会强制将修改的值立即写入主存;
第二:使用 volatile 关键字的话,当线程 2 进行修改时,会导致线程 1 的工作内存中缓存变量的缓存行无效(反映到硬件层的话,就是 CPU 的 L1 或者 L2 缓存中对应的缓存行无效);
第三:由于线程 1 的工作内存中缓存变量的缓存行无效,所以线程 1 再次读取变量的值时会去主存读取。
# 参考
https://blog.csdn.net/agonie201218/article/details/128923265
- 01
- idea 热部署插件 JRebel 安装及破解,不生效问题解决04-10
- 02
- spark中代码的执行位置(Driver or Executer)12-12
- 03
- 大数据技术之 SparkStreaming12-12