Java并发编程核心技术:synchronized、volatile与CAS
synchronized的实现原理与锁升级机制
在Java虚拟机层面,synchronized关键字通过对象头的Mark Word实现锁状态管理。每个Java对象在堆内存中都包含对象头,其中存储了锁状态、GC分代年龄等信息。
对象头的Mark Word在不同锁状态下会发生变化:
- 无锁状态:存储对象的hashCode和分代年龄
- 偏向锁:存储持有偏向锁的线程ID
- 轻量级锁:指向栈中锁记录的指针
- 重量级锁:指向互斥量(mutex)的指针
锁升级过程遵循以下路径:
- 偏向锁:当第一个线程访问同步块时,JVM启用偏向锁模式
- 轻量级锁:当第二个线程尝试获取锁时,升级为轻量级锁(自旋锁)
- 重量级锁:当自旋超过阈值(默认10次)或等待线程超过CPU核心数的一半时升级
示例代码展示不同使用场景:
// 实例方法同步
public synchronized void instanceMethod() {
// 临界区代码
}
// 静态方法同步
public static synchronized void staticMethod() {
// 临界区代码
}
// 同步代码块
public void codeBlock() {
Object lock = new Object();
synchronized(lock) {
// 临界区代码
}
}
volatile的内存语义与指令重排序
volatile关键字通过内存屏障(Memory Barrier)实现两大特性:
- 可见性保证:写操作会立即刷新到主内存
- 禁止指令重排序:编译器优化和处理器指令重排受限
JVM层面的内存屏障策略:
- LoadLoad屏障:确保本读操作先于后续读操作
- StoreStore屏障:确保本写操作先于后续写操作
- LoadStore屏障:确保读操作先于后续写操作
- StoreLoad屏障:确保写操作先于后续读操作
典型应用场景分析:
public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 写操作
}
public void reader() {
if (flag) { // 读操作
// 执行相关操作
}
}
}
CAS操作与原子类实现原理
Compare And Swap(CAS)通过CPU原子指令实现无锁编程,核心公式: CAS(V, E, N) = 当且仅当V的值等于E时,将V的值设为N
Java中的Unsafe类提供CAS底层支持:
public final class Unsafe {
public final native boolean compareAndSwapInt(
Object o, long offset, int expected, int x);
// 其他CAS方法
}
AtomicInteger源码解析:
public class AtomicInteger extends Number {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
并发工具对比与实践选择
性能对比测试数据(示例): | 操作类型 | 10线程/ms | 100线程/ms | 1000线程/ms | |---------------|---------|----------|-----------| | synchronized | 32 | 285 | 4231 | | volatile读 | 12 | 98 | 1052 | | CAS操作 | 8 | 45 | 387 |
选型决策树:
- 需要互斥访问 → 选择synchronized
- 仅需可见性保证 → 使用volatile
- 高并发计数器场景 → 优先考虑Atomic类
- 复杂原子操作 → 使用LongAdder或Accumulator
常见问题与解决方案
synchronized典型问题:
- 死锁案例:
// 线程1
synchronized(resourceA) {
synchronized(resourceB) { ... }
}
// 线程2
synchronized(resourceB) {
synchronized(resourceA) { ... }
}
解决方案:使用ReentrantLock的tryLock()方法
volatile使用误区:
// 错误用法:复合操作不具备原子性
private volatile int count = 0;
public void unsafeIncrement() {
count++; // 实际包含读-改-写三个操作
}
CAS的ABA问题解决方案:
AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(0, 0);
// 更新时同时检查值和版本戳
atomicRef.compareAndSet(0, 1, 0, 1);
底层机制深入解析
synchronized的monitor实现:
// HotSpot虚拟机源码片段
ObjectMonitor() {
_header = NULL;
_count = 0; // 重入次数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL; // 持有线程
_WaitSet = NULL; // 等待队列
_EntryList = NULL; // 竞争队列
}
内存屏障的具体实现(x86架构):
- StoreStore屏障:对应"lock; addl $0x0,(%rsp)"
- LoadLoad屏障:通过mfence指令实现
- StoreLoad屏障:使用mfence或xchg指令
CPU缓存一致性协议(MESI):
- Modified(修改):缓存行已被修改
- Exclusive(独占):缓存行未被修改
- Shared(共享):缓存行只读
- Invalid(无效):缓存行数据过期
正文到此结束
相关文章
热门推荐
评论插件初始化中...