Java自旋锁基于CAS用AtomicInteger控制0/1状态,通过循环+onSpinWait()自旋获取锁,适合短临界区低竞争场景;不支持可重入、无公平性、不可中断,高竞争下性能差。

Java 中基于 CAS 实现自定义自旋锁,核心是用 java.util.concurrent.atomic.AtomicInteger 或 AtomicReference 控制锁状态,通过循环 + CAS 尝试获取锁,不阻塞线程。它适合临界区极短、竞争不激烈的场景,避免上下文切换开销。
锁状态设计与原子变量选择
自旋锁只需两种状态:未锁定(0)和已锁定(1)。推荐使用 AtomicInteger,初始值为 0;也可用 AtomicReference<thread></thread> 实现可重入或记录持有线程(但纯自旋锁通常不考虑重入)。关键在于所有状态变更必须通过 CAS 原子完成。
-
用
AtomicInteger state:0 表示空闲,1 表示已被占用 - 获取锁时调用
state.compareAndSet(0, 1),成功即获得锁 - 释放锁时设为 0:
state.set(0)(无需 CAS,因只有持有者能释放)
自旋等待逻辑:避免忙等耗尽 CPU
单纯 while(true) 会持续占用 CPU。应在每次 CAS 失败后插入轻量级让步,例如 Thread.onSpinWait()(JDK9+)或 Thread.yield()。更优做法是结合短暂的 LockSupport.parkNanos(1),但严格来说已偏离“纯自旋”——若需平衡响应与资源,可加入指数退避。
- 基础写法:
while (!state.compareAndSet(0, 1)) { Thread.onSpinWait(); } - 简单退避示例:失败后
Thread.sleep(1)不推荐(涉及系统调用);可用for (int i = 0; i 模拟短暂停顿
完整可运行示例(非重入、无超时)
以下是一个最小可行的自定义自旋锁实现:
立即学习“Java免费学习笔记(深入)”;
<font size="2"><pre class="brush:php;toolbar:false;">public class SimpleSpinLock {
private final AtomicInteger state = new AtomicInteger(0); // 0: free, 1: locked
public void lock() {
while (!state.compareAndSet(0, 1)) {
Thread.onSpinWait(); // JDK9+ 推荐;JDK8 可用 Thread.yield()
}
}
public void unlock() {
state.set(0); // 仅持有线程应调用,不校验线程身份
}
}
使用方式类似 synchronized:
<font size="2"><pre class="brush:php;toolbar:false;">SimpleSpinLock lock = new SimpleSpinLock();
new Thread(() -> {
lock.lock();
try {
// 临界区操作
System.out.println("Thread " + Thread.currentThread().getName() + " is in critical section");
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}).start();
注意事项与局限性
自旋锁不是万能替代 synchronized。它在高竞争、长临界区下会导致严重性能下降甚至饥饿。
- 不支持可重入:同一线程重复 lock() 会死锁,需额外字段记录 owner 才能支持
- 不保证公平性:CAS 成功率依赖线程调度,先到不一定先得
- 无法响应中断:lock() 是忙等,不能被 interrupt 中断(需手动加中断检查,但破坏简洁性)
- 慎用于多核少、高负载环境:自旋消耗的是真实 CPU 时间,可能拖慢其他任务


















