一、核心锁策略
-
乐观锁 (Optimistic): “认为没冲突”:先进行操作,只有在提交更新时才去确认是否被其他线程修改过(版本号机制)。如 CAS、AtomicInteger
-
悲观锁 (Pessimistic):“认为必冲突”:每次操作数据都先加锁,确保独占资源,直到操作完成。如 synchronized 、
ReentranLock(true) -
公平锁 (Fair):“先来后到”:线程获取锁的顺序严格按照申请的顺序。如 ReentrantLock(true)
-
非公平锁 (Non-Fair):“谁抢到是谁的”:允许插队,吞吐量高,但可能导致线程饥饿。如:synchronized、
ReentrantLock(false) -
可重入锁 (Reentrant): “自己能锁自己”:同一个线程可以多次获取同一把锁,通过计数器实现。如 synchronized、
ReentrantLock -
读写锁 (Read-Write):“读共享,写独占”:读操作可以并发,写操作必须互斥。如ReentrantReadWriteLock
二、CAS:无锁并发的基石
CAS (Compare And Swap) 即比较并交换,是 CPU 提供的一条原子指令,也是乐观锁的实现基础。
-
核心原理:
-
它包含三个操作数 :V(内存值),A(预期值),B(新值)
-
逻辑:如果内存值 V 等于预期值 A,则将 V 更新为 B;否则什么都不做。这个“比较+交换”的过程是原子性的。
-
ABA问题与解决方案
-
问题:线程 T1 读取值 A,线程 T2 将 A 改为 B 又改回 A。T1 再次 CAS 时发现还是 A,于是操作成功。虽然值没变,但中间状态已被篡改。
-
解决方案:引入版本号(Stamp)。每次修改版本号递增,即使值相同,版本号不同也会导致 CAS 失败。Java 提供了
AtomicStampedReference来解决此问题。
-
应用场景
-
Java 的
Atomic包(如AtomicInteger)底层就是基于 CAS 自旋实现的。 -
并发容器如
ConcurrentHashMap。
三、synchronized 的锁升级与优化过程
synchronized 并不是一开始就是“重量级锁”。为了减少性能损耗,JVM 会根据竞争激烈程度,自动进行锁升级。这个过程是不可逆的(只能升级,不能降级)。

-
偏向锁 (Biased): “懒汉模式”。它偏向于第一个访问的线程,如果没有竞争,该线程以后每次进入同步块都不需要做任何同步操作。
-
轻量级锁 (Lightweight): 当有第二个线程竞争时,偏向锁升级。线程在栈中创建锁记录 (Lock Record),通过 CAS 将对象头指向该记录。如果 CAS 失败,说明有竞争,进入自旋。
-
自旋锁 (Spin): 轻量级锁的一种优化。线程不立即阻塞,而是循环尝试获取锁(忙等待)。如果持有锁的线程很快释放,自旋线程就能立即获取,避免了阻塞/唤醒的开销。
-
锁膨胀: 如果自旋次数超过阈值(或等待线程过多),JVM 会将轻量级锁膨胀为重量级锁,此时未获取到锁的线程会被挂起。
除了锁升级,JVM 还会在编译期进行以下优化:
-
锁消除 (Lock Elimination): JIT 编译器通过逃逸分析,如果发现共享变量不会逃逸出当前线程(如方法内的
StringBuffer),就会直接消除synchronized关键字,不加锁。 -
锁粗化 (Lock Coarsening): 如果一系列连续的操作都在对同一个对象加锁(如循环内加锁),JVM 会将锁的范围扩大(粗化),变成一次加锁解锁,减少频繁申请释放锁的开销。
278

被折叠的 条评论
为什么被折叠?



