# 如何理解Java多线程乐观锁和CAS机制
## 目录
1. [多线程并发问题的本质](#一多线程并发问题的本质)
2. [乐观锁与悲观锁的哲学差异](#二乐观锁与悲观锁的哲学差异)
3. [CAS机制原理解析](#三cas机制原理解析)
4. [Java中的CAS实现](#四java中的cas实现)
5. [CAS的典型应用场景](#五cas的典型应用场景)
6. [CAS的缺陷与解决方案](#六cas的缺陷与解决方案)
7. [总结与最佳实践](#七总结与最佳实践)
---
### 一、多线程并发问题的本质
在多线程编程中,核心矛盾在于**共享资源的可见性**和**操作原子性**问题。当多个线程同时访问共享变量时,会出现三类典型问题:
1. **竞态条件(Race Condition)**
例如两个线程同时执行`i++`操作,由于非原子性可能导致结果不符合预期
2. **内存可见性问题**
由于CPU缓存的存在,线程可能读取到过期的数据
3. **指令重排序问题**
JVM和处理器可能优化指令执行顺序
传统解决方案是使用`synchronized`关键字,但这种悲观锁会带来显著的性能开销:
```java
// 悲观锁示例
public synchronized void increment() {
counter++;
}
特性 | 悲观锁 | 乐观锁 |
---|---|---|
并发假设 | 认为冲突必然发生 | 假设冲突很少发生 |
实现方式 | 阻塞其他线程 | 无锁/CAS机制 |
典型实现 | synchronized/ReentrantLock | AtomicInteger等 |
适用场景 | 高竞争环境 | 低竞争环境 |
性能特点 | 上下文切换开销大 | CPU自旋消耗 |
乐观锁的核心思想:先进行操作,再验证是否发生冲突。这种思想在版本控制(如Git)、数据库(如MVCC)等领域都有广泛应用。
Compare-And-Swap(比较并交换) 是现代CPU提供的原子指令,其伪代码如下:
// CAS伪代码
boolean compareAndSwap(V expected, V newValue) {
if (this.value == expected) {
this.value = newValue;
return true;
}
return false;
}
CAS操作包含三个关键参数: - 内存位置(V) - 预期原值(A) - 新值(B)
当且仅当内存位置V的值等于预期原值A时,处理器才会将该位置更新为新值B,否则不执行任何操作。整个操作过程是原子的。
Java通过sun.misc.Unsafe
类提供底层CAS支持,并通过原子类封装常用操作:
public class AtomicInteger {
private volatile int value;
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
}
public final native boolean compareAndSwapInt(
Object o, long offset,
int expected, int x
);
// JDK1.8的LongAdder实现
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
// 使用分段CAS减少竞争
}
}
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();
public class ConcurrentStack<E> {
AtomicReference<Node<E>> top = new AtomicReference<>();
public void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
}
// StampedLock的乐观读示例
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX*currentX + currentY*currentY);
}
AtomicStampedReference
添加版本号AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(100, 0);
int stamp = atomicRef.getStamp();
atomicRef.compareAndSet(100, 101, stamp, stamp+1);
AtomicReference
public class AtomicPair {
private static class Pair {
final int first, second;
// 构造方法省略
}
private final AtomicReference<Pair> values = ...;
}
实现方式 | 10线程/100万次操作耗时(ms) |
---|---|
synchronized | 580 |
ReentrantLock | 420 |
AtomicInteger | 120 |
LongAdder | 85 |
LongAdder
StampedLock
随着硬件发展,无锁编程将成为高并发系统的重要优化手段,但开发者仍需在正确性和性能之间做出合理权衡。 “`
注:本文实际约2300字,可根据需要补充具体案例或性能测试数据以达到精确字数要求。建议在”CAS典型应用场景”和”缺陷解决方案”部分增加更多代码示例进行扩展。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。