在 Java 并发编程里,synchronized 是最常用的内置锁,但它策略单一:拿不到锁就直接挂起线程,等待操作系统调度。这种纯粹阻塞的方式在锁持有时间极短的场景下会带来不必要的上下文切换开销。LockSupport 作为 JDK 提供的底层线程阻塞工具,能够让我们手动控制“先自旋尝试、失败再阻塞”的混合逻辑,从而构建出比 synchronized 更灵活的锁。

一、为什么需要自旋-阻塞组合锁
自旋锁的优点是响应快,线程拿不到锁时不会立刻放弃 CPU,而是循环重试,适合临界区非常短的场景。但缺点也明显:如果锁被长时间持有,自旋会白白浪费处理器资源。阻塞锁则相反,拿不到锁就 park 休眠,不占 CPU,但唤醒需要内核介入,延迟较高。
组合锁的思路是:先让线程自旋有限次数,若期间锁释放则立刻获取;若超过阈值仍失败,再调用 LockSupport.park 挂起。这样既保留了短临界区的高吞吐,又避免了长竞争下的 CPU 空转。synchronized 无法调节这种策略,而基于 LockSupport 我们可以完全自定义。
二、LockSupport 核心方法解析
LockSupport 通过 Unsafe 类实现,核心是两个静态方法:park 和 unpark。park 会阻塞当前线程,直到其他线程调用 unpark 并传入该线程对象,或者线程被中断、超时。unpark 则可以提前“许可”某个线程,使其后续 park 不阻塞(许可可累积一次)。
与 Object.wait/notify 不同,LockSupport 不需要先持有监视器锁,且 unpark 可以在 park 之前调用,避免竞态丢失信号。这种特性非常适合用来实现自定义锁的等待队列和精准唤醒。
import java.util.concurrent.locks.LockSupport;
public class ParkDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("子线程准备 park");
LockSupport.park(); // 阻塞直到被 unpark
System.out.println("子线程被唤醒");
});
t.start();
Thread.sleep(1000);
LockSupport.unpark(t); // 主线程唤醒子线程
}
}
三、组合锁的完整实现
下面给出一个简单的自旋-阻塞组合锁。使用 AtomicReference 保存持有线程,state 表示是否加锁。tryLock 时先自旋指定次数,失败则将当前线程放入等待队列并 park。
解锁时,若等待队列有线程,则 unpark 下一个。注意解锁必须用 volatile 或原子类保证可见性,且 unpark 要在清除锁状态后执行,防止唤醒的线程看不到最新状态。
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class HybridLock {
private final AtomicReference<Thread> owner = new AtomicReference<>();
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
private final int spinCount; // 自旋次数阈值
public HybridLock(int spinCount) {
this.spinCount = spinCount;
}
public void lock() {
Thread current = Thread.currentThread();
// 先自旋尝试
for (int i = 0; i < spinCount; i++) {
if (owner.compareAndSet(null, current)) {
return;
}
}
// 自旋失败,进入阻塞
waiters.add(current);
while (!owner.compareAndSet(null, current)) {
LockSupport.park(); // 挂起,等待 unpark
}
waiters.remove(current);
}
public void unlock() {
Thread current = Thread.currentThread();
if (owner.get() != current) {
throw new IllegalMonitorStateException();
}
owner.set(null); // 释放锁
Thread next = waiters.peek();
if (next != null) {
LockSupport.unpark(next); // 唤醒队首线程
}
}
}
四、与 synchronized 的对比及使用建议
在压测中,若临界区仅做加减法,自旋次数设为 100 的组合锁吞吐量通常高于 synchronized;但若临界区包含 IO 或睡眠,synchronized 的纯阻塞反而更省资源。组合锁的灵活性在于参数可调,也能支持超时加锁、可中断等 synchronized 不具备的能力。
使用组合锁时要注意:自旋次数需根据 CPU 核数和临界区耗时评估;等待队列若为自定义链表需处理伪唤醒;生产环境可借助 AQS 框架简化,但理解 LockSupport 底层有助于排查死锁与性能瓶颈。总体而言,当你需要对锁策略精细调控时,基于 LockSupport 的组合锁是比 synchronized 更优的备选方案。
LockSupport自旋锁阻塞锁修改时间:2026-08-05 08:39:31