1.JMM内存模型(Java Memory Model)
1.JMM定义
2.如何体现在内存硬件
关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作, 如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
并发的本质到底是什么!!! 请继续往下看
2.并发三大特性
并发编程Bug的源头:可见性、原子性和有序性问题
可见性和有序性
当共享变量的值被一个线程修改时,其他线程可以看到被修改的值,java内存模型是通过在变量修改后将新值同步回主内存,同时让其他的线程可以读取到最新内存的值。
如何保证可见性和有序性
- volatile 关键字
- synchronized
- Lock锁
此外final也可以满足可见性!!!
Load屏障:执行读取数据的时候,强制每次都从主内存读取最新的值。
Store屏障:每次执行修改数据的时候,强制刷新回主内存。
加synchronized后,底层是通过monitorenter的指令来进行加锁的、通过monitorexit指令来释放锁的。
monitorenter指令还具有Load屏障的作用,synchronized内部的共享变量,每次读取数据时强制从主内存读取最新的数据。
lock锁的话本身是通过会去修改AQS里面的state,state使用volatile去修饰的,因此相当于加入内存屏障。
内存屏障会强制
原子性
原子性(atomicity):指一个操作是不可分割的、完整的,要么全部执行成功,要么全部不执行,不存在执行一半的情况
在 Java 中,对基本数据类型的变量的读取和赋值操作是原子性操作(64位处理器)。不采取任何的原子性保障措施的自增操作并不是原子性的。
比如:i++其实并不是原子操作,也就是说,它不是单独一条指令,而是3条指令:
1、从内存中把i的值取出来放到CPU的寄存器中
2、CPU寄存器的值+1
3、把CPU寄存器的值写回内存
如何保证原子性
- synchronized
- Lock
因为被 synchronized 修饰某段代码 后,无论是单核 CPU 还是多核 CPU,只有一个线程能够执行该代码,所以一定能保证原子操作.f
- CAS+volatile,
通过自旋锁和可见性,该算法的核心是硬件对于并发操作的支持。
咱讲了这么多原理,开始搞事情了,那我们先从volatile搞起,兄弟们,跟我冲!!!
3.再次深度理解volatile
1.volatile可见性底层原理
1.特性
volatile修饰的变量的read、load、use操作和assign、store、write必须是连续的,即修改后必须立即刷新主内存,使用时必须从主内存中重新获取,由此保证volatile变量操作对多线程的可见性。
2.硬件层面
通过lock前缀指令,会锁定变量缓存行区域并写回主内存,这个操作称为“缓存锁定”,假设一个处理器的缓存被修改,会回写到内存并导致其他处理器的缓存无效(MESI缓存一致性协议),其他线程在读取数据时,只能从内存中获取。
2.指令重排序
1.什么是指令重排序
指令重排序是指编译器或CPU为了优化程序的执行性能而对指令进行重新排序的一种手段,其实就是在多线程开发中写的代码顺序和执行代码的顺序不一致。
指令重排序的意义:JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。
2.指令重排序demo
package multiThread;
public class ReOrderingDemo {
//定义四个静态变量
private static int x=0,y=0;
private static int a=0,b=0;
public static void main</