如果要理解多线程问题首先要了解多线程是如何工作的
主内存与工作内存:
线程,工作内存,主内存三者交互关系图:
主内存:可以线程共享的变量(会出现竞争关系) 包括实例字段 静态字段 数组对象的元素(此处与Java中还有所不同)(不包括局部变量与方法参数)变量必须在主内存中产生
工作内存:该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取赋值)都必须在工作内存中进行,不能直接读取主内存中的变量,不同的线程间也无法直接访问对方工作内存中的变量,线程间变量值的传递都需要通过主内存来完成
注:JMM 与 JVM 中堆栈二者是没有关系;如果二者要强硬对上,那么主内存主要对应着Java堆与方法区,工作内存对应着虚拟机栈部分 虚拟机甚至可能会让工作内存优先存储于寄存器和高速缓存中。
内存间交互操作:
一个变量如何从主内存拷贝到工作内存中、以及工作内存如何同步到主内存中,Java内存模型中定义了 8 种操作来完成的,虚拟机实现每一种操作都是原子的,不可分割的。(当线程对变量进行了操作这 6 步都必须按序执行完成 不一定连续 如果未加锁的话 可能会不同线程的不同操作交叉执行)
1.read (读取):作用于主内存的变量,将一个变量的值从主内存传输到工作内存中
2.load(载入):作用于工作内存的变量,将read操作得到的变量的值放入工作内存的变量副本中
3.use(使用):作用于工作内存的变量,把工作内存中的变量交给执行引擎处理 虚拟机遇到使用变量会处理
4.assign(赋值):作用于工作内存的变量,把执行引擎收到的值赋给工作变量,虚拟机遇到赋值操作会执行
5.store(存储):作用于工作内存的变量,将工作内存中变量的值传给主内存
6.read(写入):作用于主内存中的变量,将工作内存中变量的值放入主内存中
如果有加锁操作还会执行下面两步:(多次执行 lock 操作 必须要执行相同次数的 unlock 才会解锁 不允许对未lock的变量unlock 也不允许去unlock一个被其他线程锁住的变量)如同 锁 与 钥匙 的关系 一把钥匙 只能开一把锁不能开其他锁
1.lock(锁定):作用于主内存的变量,将一个变量标识为一条线程独占(且会清空工作变量的值,重新执行)
2.unlock(解锁):作用于主内存的变量,将锁定状态的变量释放出来,才可继续被其他线程使用
特别解析:volatile 型变量特殊规则
1.此变量对所有线程具有可见性(可以认为不存在不一致的情况 但是并发下线程不安全的):当一个线程修改了该变量的值,都必须同步回主内存,这个新得到的值对于所有线程都是可以立即知道的(所有线程要使用该变量的值 都必须先从主内存刷新到最新的值) 而普通变量则做不到
2.禁止指令重排优化
首先为什么会指令重排呢?
各个变量内存中存储并不是连续的(只是为了便于研究将内存中的区域划分为一整块一整块的)为了使运算器的处理单元尽可能的充分利用,处理器可能会对输入的代码进行乱序执行优化,但保证执行结果和未进行优化的结果是一致的
拓展:Java内存模型要求如上 8 个操作必须具有原子性,不可分割。但允许将没有被 volatile 修饰的 64 的数据类型(double,float)读写操作分两步进行 也就是有可能读到 "半个变量"的值 但在虚拟机层面上又将读写操作实现成了原子操作 --(故可以不考虑这方面的影响)
Java内存模型并发操作需要实现的三个特征:
原子性:Java内存模型来直接保证原子性变量的操作包括 read、load、assign、store 和 write ;Java的基本类型读写操作可认为是具有原子性的
如果我们想要从更大范围而不仅仅是从单个变变量考虑时,Java 为我们提供了 lock 和 unlock 操作 尽管虚拟机未向用户直接开放,但是可以使用同步块 synchronized 关键字(底层反应的字节码指令是 monitorenter(翻译:进入监控 ? )monitorexit(翻译 :退出监控 ? ))
可见性:可见性是一个线程修改了共享变量的值,其他变量能够立即知道(除 volatile 以外还有 synchronizd 和 final )
synchronizd 的可见性:“ 一个变量执行 unlock 操作之前必须先把此变量同步到主内存中 ”
final 的可见性:一旦初始化,其他线程就可以通过 this 访问到 且是不可变的
有序性:如果在一个在线程内看所有操作都是有序的(线程表现为串行化)在更宏观来看,所有操作都是无序的(指令重排优化,工作内存和主内存变量的值同步延迟 );在Java中由 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性
volatile 的有序性:禁止指令重排优化
synchronized的有序性:“一个变量在同一时刻只允许一条线程对其进行 lock 操作”
---- 参考《深入理解Java虚拟机:JVM高级特性与最佳实践》 ---