Java中高效可重入自旋锁的核心是CAS更新状态变量并记录持有线程与重入次数,推荐用AtomicLong高32位存线程ID、低32位存计数,加锁时区分首次获取与重入,解锁时严格校验线程身份与计数,辅以自旋上限等优化。

Java 中用 CAS 结合自旋锁实现一个高效且可重入的自定义锁,核心在于两点:一是用 CAS 原子更新状态变量(避免阻塞),二是记录持有线程和重入次数(支持同一线程多次加锁)。它不是简单套用 AtomicReference 就完事,而是要模拟 ReentrantLock 的关键行为逻辑——但更轻量、无 AQS 队列开销。
状态设计:用 int 编码线程标识 + 重入计数
不能只靠 AtomicInteger state 判断是否加锁成功,否则无法区分“未加锁”和“被其他线程持有”。推荐把线程 ID 和重入次数打包进一个 long 或用两个原子变量协同管理:
- 方案一(推荐):用
AtomicLong state,高 32 位存线程 ID(threadId),低 32 位存重入次数(count) - 方案二:用
AtomicReference<Thread> owner+AtomicInteger count,但需保证两者更新的原子性(可用compareAndSet配合循环重试)
例如,获取当前状态:long s = state.get(); long tid = s >> 32; int cnt = (int) s;;更新时用 CAS 循环尝试写入新值。
加锁逻辑:区分首次获取与重入
每次调用 lock() 时,先检查当前线程是否已是持有者:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 如果是:仅将重入计数 +1,直接返回
- 如果不是:尝试用 CAS 把 state 设为「当前线程 ID + 1」;失败则进入自旋(可加有限次数限制,避免 CPU 空转过久)
示例关键片段:
if (Thread.currentThread() == owner.get()) { count.incrementAndGet(); return; }while (true) {
if (state.compareAndSet(0, ((long) Thread.currentThread().getId() << 32) | 1L)) {
owner.set(Thread.currentThread());
count.set(1);
break;
}
// 可选:自旋一定次数后 yield() 或短暂 sleep()
}
解锁逻辑:必须校验线程身份
可重入锁的解锁不能无脑减计数——必须确保是持有线程在调用 unlock():
- 若当前线程 ≠ 持有者:抛
IllegalMonitorStateException - 若重入计数 > 1:仅减 1,不释放锁
- 若重入计数 == 1:CAS 将 state 清零(或设为 0),并清空 owner 引用
这一步防止误解锁或跨线程释放,是安全性的关键防线。
额外优化点
让这个锁真正“高效”,还需考虑工程细节:
-
自旋上限:比如最多自旋 100 次,之后退避为
Thread.yield()或LockSupport.parkNanos(1),避免单核满载 -
公平性开关:默认非公平(类似 ReentrantLock),如需公平,可引入简单队列(如
AtomicReference<Node>)做 FIFO 调度,但会增加复杂度 -
tryLock 支持:提供带超时或无等待的
tryLock(),底层复用同一套 CAS 逻辑,失败直接返回 false -
不可中断性:纯自旋锁默认不响应
interrupt();如需支持,可在自旋中定期检查Thread.interrupted()
这种实现比 synchronized 更灵活,比 ReentrantLock 更轻量,适合高竞争但临界区极短的场景。不过要注意:它仍是用户态忙等,长时间持锁会导致 CPU 浪费,所以务必控制好临界区执行时间。

















