Java并发-LockSupport线程阻塞工具类
LockSupport
是 Java 并发包中一个非常底层且强大的线程阻塞工具类,它为线程的阻塞和唤醒提供了更加灵活和高效的机制。
# 一、为什么需要 LockSupport
在 Java 中,让线程暂停有多种方式:
Object.wait()
- 需要先获取对象锁,必须在synchronized
块中使用Thread.sleep()
- 让线程睡眠指定时间,不能被其他线程唤醒Thread.suspend()
- 已废弃,线程挂起时不释放锁资源,容易导致死锁
这些方法都有各自的限制。LockSupport
提供了一种更加灵活的线程阻塞机制:
- 不需要获取锁就可以让线程阻塞
- 可以指定阻塞的线程,精确唤醒
- 基于许可(permit)机制,避免了死锁问题
- 是构建同步组件的基础工具(
AQS
就是基于它实现的)
# 二、基本使用
先来看一个简单的示例:
public class LockSupportDemo {
static LsThread t1 = new LsThread("t1");
static LsThread t2 = new LsThread("t2");
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(1000);
t2.start();
LockSupport.unpark(t1);
LockSupport.unpark(t2);
t1.join();
t2.join();
}
}
class LsThread extends Thread{
public LsThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("in "+getName());
LockSupport.park(this);
System.out.println("out " +getName());
}
}
运行结果:
in t1
in t2
out t1
out t2
在这个例子中,t1
和 t2
线程启动后都会调用 park()
方法暂停,主线程通过 unpark()
方法唤醒它们。
# 三、核心方法详解
# 1、park() 方法族
// 阻塞当前线程,如果有许可则消耗许可并立即返回
public static void park()
// 阻塞当前线程,传入 blocker 对象用于监控和诊断
public static void park(Object blocker)
// 阻塞当前线程,最长等待 nanos 纳秒
public static void parkNanos(long nanos)
// 阻塞当前线程,直到 deadline 时间点
public static void parkUntil(long deadline)
# 2、unpark() 方法
// 给指定线程一个许可,如果线程已阻塞则唤醒它
public static void unpark(Thread thread)
# 四、许可机制(Permit)
LockSupport
的核心是基于许可(permit)的机制:
- 每个线程都有一个许可,初始值为 0
- 调用
unpark()
方法会将许可设置为 1(多次调用不会累加) - 调用
park()
方法时:- 如果许可为 1,则消耗许可(设为 0)并立即返回
- 如果许可为 0,则线程阻塞
# 1、许可机制示例
public class PermitDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("线程开始执行");
// 先 unpark,获得许可
LockSupport.unpark(Thread.currentThread());
// 调用 park,因为有许可,所以不会阻塞
LockSupport.park();
System.out.println("第一次 park 后继续执行");
// 再次调用 park,没有许可了,会阻塞
LockSupport.park();
System.out.println("第二次 park 后继续执行");
});
thread.start();
Thread.sleep(2000);
// 唤醒阻塞的线程
LockSupport.unpark(thread);
thread.join();
}
}
# 五、实际应用场景
# 1、实现简单的同步器
public class SimpleLatch {
private volatile boolean released = false;
public void await() {
while (!released) {
LockSupport.park(this);
}
}
public void release() {
released = true;
// 唤醒所有等待的线程
for (Thread thread : Thread.getAllStackTraces().keySet()) {
LockSupport.unpark(thread);
}
}
}
# 2、实现生产者消费者模式
public class ProducerConsumer {
private final Queue<String> queue = new LinkedList<>();
private final int capacity = 5;
private Thread consumer;
private Thread producer;
public void produce(String item) {
synchronized (queue) {
while (queue.size() >= capacity) {
LockSupport.park(this);
}
queue.offer(item);
System.out.println("生产: " + item);
}
// 唤醒消费者
LockSupport.unpark(consumer);
}
public String consume() {
String item;
synchronized (queue) {
while (queue.isEmpty()) {
LockSupport.park(this);
}
item = queue.poll();
System.out.println("消费: " + item);
}
// 唤醒生产者
LockSupport.unpark(producer);
return item;
}
}
# 3、响应中断的阻塞
public class InterruptiblePark {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("线程开始 park");
LockSupport.park();
// park 方法响应中断但不抛出异常
if (Thread.interrupted()) {
System.out.println("线程被中断了");
}
System.out.println("线程继续执行");
});
thread.start();
Thread.sleep(1000);
// 中断线程,park 会响应中断
thread.interrupt();
thread.join();
}
}
# 六、与其他同步工具的对比
特性 | LockSupport | Object.wait() | Thread.sleep() | Condition.await() |
---|---|---|---|---|
需要获取锁 | 否 | 是 | 否 | 是 |
可以响应中断 | 是(不抛异常) | 是(抛异常) | 是(抛异常) | 是(抛异常) |
可以设置超时 | 是 | 是 | 是 | 是 |
精确唤醒 | 是 | 否(notifyAll) | 不支持唤醒 | 是 |
使用复杂度 | 简单 | 中等 | 简单 | 复杂 |
# 七、使用注意事项
# 1、许可不会累加
// 多次 unpark 只会给一个许可
LockSupport.unpark(thread);
LockSupport.unpark(thread); // 第二次调用无效
// 线程只能消耗一个许可
LockSupport.park(); // 消耗许可
LockSupport.park(); // 会阻塞
# 2、park 方法可能无理由返回
// park 可能会"虚假唤醒",应该在循环中使用
while (!condition) {
LockSupport.park(this);
}
# 3、使用 blocker 参数便于诊断
// 推荐传入 blocker 对象,便于通过诊断工具查看
LockSupport.park(this); // 好
LockSupport.park(); // 不推荐
# 4、注意内存可见性
public class VisibilityDemo {
// 使用 volatile 确保可见性
private volatile boolean flag = false;
public void waitForFlag() {
while (!flag) {
LockSupport.park(this);
}
}
public void setFlag() {
flag = true;
// 唤醒等待的线程
LockSupport.unpark(waitingThread);
}
}
# 八、最佳实践
优先使用高层同步工具:如
CountDownLatch
、Semaphore
等,它们基于LockSupport
实现但提供了更高级的抽象在循环中检查条件:避免虚假唤醒导致的问题
合理使用 blocker 参数:有助于问题诊断和调试
注意线程中断状态:
park
响应中断但不清除中断状态配合 volatile 使用:确保状态的内存可见性
# 九、总结
LockSupport
是 Java 并发包的基础工具,它提供了灵活、高效的线程阻塞和唤醒机制。虽然它很强大,但在实际开发中,我们通常使用基于它构建的更高层次的同步工具。理解 LockSupport
的工作原理有助于我们更好地理解和使用 Java 并发包中的其他组件。
编辑 (opens new window)
上次更新: 2025/08/14