@@ -48,12 +48,12 @@ public native void fullFence();
4848
4949JMM(Java 内存模型)定义了 4 种内存屏障(Memory Barrier),用于控制特定条件下的指令重排序和内存可见性:
5050
51- | 屏障类型 | 指令示例 | 说明 |
52- | --- | --- | --- |
53- | ** LoadLoad** | ` Load1; LoadLoad; Load2 ` | 保证 ` Load1 ` 的读取操作在 ` Load2 ` 及其后续读取操作之前完成 |
54- | ** StoreStore** | ` Store1; StoreStore; Store2 ` | 保证 ` Store1 ` 的写入操作对其他处理器可见(刷新到内存),先于 ` Store2 ` 及其后续写入操作 |
55- | ** LoadStore** | ` Load1; LoadStore; Store2 ` | 保证 ` Load1 ` 的读取操作在 ` Store2 ` 及其后续写入操作刷新到内存之前完成 |
56- | ** StoreLoad** | ` Store1; StoreLoad; Load2 ` | 保证 ` Store1 ` 的写入操作对其他处理器可见,先于 ` Load2 ` 及其后续读取操作。` StoreLoad ` 屏障的开销是四种屏障中最大的,它同时具有其他三种屏障的效果,因此也称为 ** 全能屏障(Full Barrier)** |
51+ | 屏障类型 | 指令示例 | 说明 |
52+ | -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- |
53+ | ** LoadLoad** | ` Load1; LoadLoad; Load2 ` | 保证 ` Load1 ` 的读取操作在 ` Load2 ` 及其后续读取操作之前完成 |
54+ | ** StoreStore** | ` Store1; StoreStore; Store2 ` | 保证 ` Store1 ` 的写入操作对其他处理器可见(刷新到内存),先于 ` Store2 ` 及其后续写入操作 |
55+ | ** LoadStore** | ` Load1; LoadStore; Store2 ` | 保证 ` Load1 ` 的读取操作在 ` Store2 ` 及其后续写入操作刷新到内存之前完成 |
56+ | ** StoreLoad** | ` Store1; StoreLoad; Load2 ` | 保证 ` Store1 ` 的写入操作对其他处理器可见,先于 ` Load2 ` 及其后续读取操作。` StoreLoad ` 屏障的开销是四种屏障中最大的,它同时具有其他三种屏障的效果,因此也称为 ** 全能屏障(Full Barrier)** |
5757
5858#### volatile 读写操作的内存屏障插入策略
5959
@@ -181,7 +181,7 @@ public class VolatileHappensBeforeDemo {
181181
182182根据 ** 传递性** :操作1、操作2 happens-before 操作5、操作6。
183183
184- 因此,当线程 B 在操作4 读取到 ` flag == true ` 时,线程 A 在操作3 之前对 ` a ` 和 ` b ` 的修改对线程 B 一定是可见的。这里的关键在于:** volatile 变量的写-读操作,不仅保证了 volatile 变量本身的可见性,还通过 happens-before 的传递性"顺带" 保证了其前后普通变量的可见性。**
184+ 因此,当线程 B 在操作4 读取到 ` flag == true ` 时,线程 A 在操作3 之前对 ` a ` 和 ` b ` 的修改对线程 B 一定是可见的。这里的关键在于:** volatile 变量的写-读操作,不仅保证了 volatile 变量本身的可见性,还通过 happens-before 的传递性“顺带” 保证了其前后普通变量的可见性。**
185185
186186这也解释了为什么在实际开发中,` volatile ` 经常被用作 ** 状态标志位** (如上面例子中的 ` flag ` ),它可以在不使用锁的情况下,安全地在线程间传递状态信息,同时保证相关数据的可见性。
187187
@@ -338,7 +338,7 @@ sum.increment();
3383381 . 操作员 A 此时将其读出( ` version ` =1 ),并从其帐户余额中扣除 $50( $100-\$ 50 )。
3393392 . 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( ` version ` =1 ),并从其帐户余额中扣除 $20 ( $100-\$ 20 )。
3403403 . 操作员 A 完成了修改工作,将数据版本号( ` version ` =1 ),连同帐户扣除后余额( ` balance ` =\$ 50 ),提交至数据库更新,此时由于提交数据版本等于数据库记录当前版本,数据被更新,数据库记录 ` version ` 更新为 2 。
341- 4 . 操作员 B 完成了操作,也将版本号( ` version ` =1 )试图向数据库提交数据( ` balance ` =\$ 80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
341+ 4 . 操作员 B 完成了操作,也将版本号( ` version ` =1 )试图向数据库提交数据( ` balance ` =\$ 80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 ” 的乐观锁策略,因此,操作员 B 的提交被驳回。
342342
343343这样就避免了操作员 B 用基于 ` version ` =1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。
344344
@@ -730,13 +730,13 @@ Open JDK 官方声明:[JEP 374: Deprecate and Disable Biased Locking](https://
730730
731731二者性能差异的根本原因在于底层实现机制不同:
732732
733- | 对比维度 | ` volatile ` | ` synchronized ` |
734- | --- | --- | --- |
735- | ** 实现层面** | 通过插入内存屏障指令实现,不涉及线程阻塞和上下文切换 | 依赖操作系统的互斥锁(Mutex Lock),涉及用户态与内核态的切换 |
736- | ** 读操作开销** | 与普通变量几乎相同 | 需要获取 monitor 锁,即使无竞争也有一定开销(偏向锁/轻量级锁 CAS) |
737- | ** 写操作开销** | 需要插入 ` StoreStore ` + ` StoreLoad ` 内存屏障,有一定开销但不会导致线程阻塞 | 需要获取和释放 monitor 锁,有竞争时会导致线程阻塞和上下文切换 |
738- | ** 竞争时的表现** | 不会导致线程阻塞,始终是非阻塞的 | 线程竞争激烈时,会频繁发生阻塞和唤醒,上下文切换开销大 |
739- | ** 功能范围** | 只能修饰变量,只保证可见性和有序性 | 可以修饰方法和代码块,同时保证可见性、有序性和原子性 |
733+ | 对比维度 | ` volatile ` | ` synchronized ` |
734+ | ---------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------- --- |
735+ | ** 实现层面** | 通过插入内存屏障指令实现,不涉及线程阻塞和上下文切换 | 依赖操作系统的互斥锁(Mutex Lock),涉及用户态与内核态的切换 |
736+ | ** 读操作开销** | 与普通变量几乎相同 | 需要获取 monitor 锁,即使无竞争也有一定开销(偏向锁/轻量级锁 CAS) |
737+ | ** 写操作开销** | 需要插入 ` StoreStore ` + ` StoreLoad ` 内存屏障,有一定开销但不会导致线程阻塞 | 需要获取和释放 monitor 锁,有竞争时会导致线程阻塞和上下文切换 |
738+ | ** 竞争时的表现** | 不会导致线程阻塞,始终是非阻塞的 | 线程竞争激烈时,会频繁发生阻塞和唤醒,上下文切换开销大 |
739+ | ** 功能范围** | 只能修饰变量,只保证可见性和有序性 | 可以修饰方法和代码块,同时保证可见性、有序性和原子性 |
740740
741741** 选择建议:**
742742
0 commit comments