前言
上一节了解synchronized 关键字的底层原理以及锁的升级过程,本节带着大家了解CPU多核硬件架构以及Java内存模型
CPU多核硬件架构剖析
CPU每次从主内存读取数据比较慢,CPU通常涉及多级缓存。CPU读主内存的数据,
按照空间局部性原则加载局部快照到缓存中
L1 L2 属于每个CPU中都是独立的缓存,缓存主内存共享变量的数据作为副本,L3属于多个cpu之间共享的缓存。
每个cpu之间都有独立二级缓存主内存的数据作为副本,而副本与副本之间是完全不可见的
总线仲裁机制
- 每次处理器和内存之间的数据传递都是通过一系列步骤来完成的,这一系列步骤称之为总线事务(Bus Transaction)
- 总线会同步试图并发使用总线的事务。在一个处理器执行总线事务期间,总线会禁止其他的处理器和I/O设备执行内存的读/写
- 总线的这种工作机制可以把所有处理器对内存的访问以串行化的方式来执行
- 在任意时间点,最多只能有一个处理器可以访问内存。这个特性确保了单个总线事务之中的内存读/写操作具有原子性
- 处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性
总线锁定
- 总线锁定就是使用处理器提供的一个 LOCK#信号,当其中一个处理器在总线上输出此信号时,其它处理器的请求将被阻塞住,那么该处理器可以独占共享内存
- 总线锁定会将并行的程序,变为串行
缓存锁定
- 缓存锁定是某个CPU对缓存数据进行更改时,会通知缓存了该数据的该数据的CPU抛弃缓存的数据或者从内存重新读取
- 在读写时要根据协议来进行操作,这类协议有MSI、MESI、MOSI、Synapse、Firefly及DragonProtocol等等,但是用的最多的就是MESI。缓存一致性协议会锁缓存行,其性能要比锁总线要高得多
MESI协议
- M 修改 (Modified) 这行数据有效,数据被修改了,和主内存中的数据不一致,数据只存在于本Cache中。
- E 独享、互斥 (Exclusive) 这行数据有效,数据和主内存中的数据一致,数据只存在于本Cache中。
- S 共享 (Shared) 这行数据有效,数据和主内存中的数据一致,数据存在于很多Cache中。
- I 无效 (Invalid) 这行数据无效。
JMM内存模型
主内存
存放我们共享变量的数据
工作内存
每个CPU对共享变量(主内存)的副本
JMM八大同步规范
- read(读取):从主内存读取数据
- load(载入):将主内存读取到的数据写入工作内存中
- use(使用):从工作内存读取数据来计算
- assign(赋值):将计算好的值重新赋值到工作内存中
- store(存储):将工作内存数据写入主内存
- write(写入):将store过去的变量值赋值给主内存中的变量
- lock(锁定):将主内存变量加锁,标识位线程独占状态
- unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量
并发三大特性
可见性、原子性、有序性
可见性
可见性指的是当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值
如何保证可见性
- 通过 volatile 关键字保证可见性
- 通过 内存屏障保证可见性
- 通过 synchronized 关键字保证可见性。
- 通过Lock保证可见性
- 通过 final 关键字保证可见性
有序性
即程序执行的顺序按照代码的先后顺序执行。JVM 存在指令重排,所以存在有序性问题。多
线程环境下,操作都是无序的,存在指令重排现象和工作内存与主内存同步延迟现象
如何保证有序性
- 通过 volatile 关键字保证可见性
- 通过 内存屏障保证可见性
- 通过 synchronized关键字保证有序性
- 通过Lock保证有序性
原子性
原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会
被其他线程影响
如何保证原子性
- 通过 synchronized 关键字保证原子性
- 通过 Lock保证原子性
- 通过 CAS保证原子性
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。