当前位置: 首页 > news >正文

LockSupport详解以及使用

LockSupport 是 Java 并发编程中一个非常基础但极其重要的工具类,它是 java.util.concurrent(JUC)包的底层核心之一,被广泛用于线程阻塞与唤醒。


🔍 一、LockSupport 是什么?

LockSupportJDK 提供的线程阻塞和唤醒工具类,位于 java.util.concurrent.locks 包中。

它不像 synchronizedReentrantLock 那样直接用于同步控制,而是提供了一种更底层、更灵活的线程挂起(park)和唤醒(unpark)机制

核心方法

  • LockSupport.park():阻塞当前线程
  • LockSupport.unpark(Thread thread):唤醒指定线程

🧱 二、为什么需要 LockSupport?对比传统方式

特性wait()/notify()LockSupport.park()/unpark()
所属机制synchronizedJUC 并发包底层
是否需要锁✅ 必须在 synchronized 块中❌ 不需要
唤醒是否必须在阻塞后❌ 唤醒前调用 notify() 会丢失✅ 可以先 unpark(),再 park()
唤醒指定线程❌ 只能随机唤醒一个或全部✅ 可指定具体线程
精确控制❌ 不够灵活✅ 更加灵活、可靠
底层支持Object 监视器Unsafe 类 + 线程许可(permit)机制

🧩 三、核心原理:许可(Permit)机制

LockSupport 的核心是 “许可”(Permit) 概念:

  • 每个线程都有一个与之关联的许可(boolean 类型,0 或 1)
  • 调用 unpark(thread):给该线程发放一个许可(最多只有一个,重复调用无效)
  • 调用 park():尝试消耗这个许可
    • 如果有许可 → 立即返回(不阻塞)
    • 如果无许可 → 阻塞,直到其他线程调用 unpark() 给它一个许可

关键特性unpark() 可以在 park() 之前调用,不会丢失!


🧪 四、基本使用示例

✅ 示例 1:最简单的 park/unpark

public class LockSupportDemo {public static void main(String[] args) throws InterruptedException {Thread worker = new Thread(() -> {System.out.println("线程 " + Thread.currentThread().getName() + " 开始工作");// 阻塞自己System.out.println("线程 " + Thread.currentThread().getName() + " 即将 park");LockSupport.park(); // 阻塞System.out.println("线程 " + Thread.currentThread().getName() + " 被唤醒,继续执行");}, "Worker");worker.start();Thread.sleep(2000); // 主线程等待 2 秒// 唤醒 worker 线程System.out.println("主线程即将 unpark Worker");LockSupport.unpark(worker);worker.join(); // 等待 worker 结束}
}

输出
在这里插入图片描述


✅ 示例 2:unpark()park() 之前调用(不会丢失)

Thread worker = new Thread(() -> {System.out.println("Worker 线程开始");// 先 unpark 再 park,不会阻塞!LockSupport.unpark(Thread.currentThread());System.out.println("Worker 线程即将 park");LockSupport.park(); // 因为已有许可,立即返回,不阻塞System.out.println("Worker 线程继续执行");
});worker.start();
worker.join();

✅ 输出中不会卡住,说明 unpark() 提前调用有效。

在这里插入图片描述


✅ 示例 3:多次 unpark() 只保留一个许可

Thread worker = new Thread(() -> {LockSupport.unpark(Thread.currentThread()); // 第一次LockSupport.unpark(Thread.currentThread()); // 第二次(无效)System.out.println("许可已发放");LockSupport.park(); // 消耗许可,继续System.out.println("第一次 park 返回");LockSupport.park(); // 再次 park,这次会阻塞(因为无许可)System.out.println("第二次 park 返回(不会执行)");
});worker.start();
Thread.sleep(3000);
// 主动唤醒,否则线程会一直阻塞
LockSupport.unpark(worker);

✅ 说明:许可不会累积,最多只有一个

在这里插入图片描述


🛠 五、高级用法与参数

1. park(Object blocker):带阻塞原因的 park

LockSupport.park("等待数据库连接"); // blocker 对象,用于诊断
  • blocker 参数通常是一个对象,表示线程为什么被阻塞
  • 在线程 dump(如 jstack)中可以看到阻塞原因,极大提升调试能力
Thread worker = new Thread(() -> {System.out.println("线程开始");LockSupport.park("等待任务队列有任务"); // 便于排查死锁System.out.println("被唤醒");
});

✅ 推荐始终使用 park(blocker) 而不是无参版本。


2. parkNanos(long nanos):限时阻塞

LockSupport.parkNanos(1_000_000_000); // 阻塞最多 1 秒
  • 阻塞指定纳秒数,超时自动唤醒
  • 类似 Thread.sleep(),但可被 unpark() 提前唤醒

3. parkUntil(long deadline):阻塞到某个时间点

long deadline = System.currentTimeMillis() + 5000; // 5 秒后
LockSupport.parkUntil(deadline);

🏗 六、LockSupport 在 JUC 中的应用(源码级解析)

LockSupportAQS(AbstractQueuedSynchronizer) 的底层支撑,几乎所有 JUC 同步器都基于它:

同步器使用方式
ReentrantLockacquire() 中调用 LockSupport.park() 阻塞线程
Semaphore获取信号量失败时 park
CountDownLatchawait() 时 park,countDown() 时 unpark
ThreadPoolExecutor工作线程空闲时 park

🔍 源码片段(简化版 AQS 获取锁逻辑)

final boolean acquireQueued(final Node node, int arg) {boolean interrupted = false;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);return interrupted;}// 阻塞当前线程LockSupport.park(this); // 核心在这里!if (Thread.interrupted()) interrupted = true;}} catch (Throwable t) {cancelAcquire(node);throw t;}
}

✅ 当线程获取锁失败时,AQS 会将其加入等待队列,并调用 LockSupport.park() 阻塞。


⚠️ 七、常见问题与注意事项

❌ 问题 1:park() 不会响应中断?

  • LockSupport.park() 不会抛出 InterruptedException
  • 但它会设置线程的中断状态Thread.interrupted() 返回 true)
Thread worker = new Thread(() -> {System.out.println("即将 park");LockSupport.park();System.out.println("被唤醒,中断状态: " + Thread.currentThread().isInterrupted());
});worker.start();
worker.interrupt(); // 中断

✅ 输出:被唤醒,中断状态: true

✅ 所以:park() 不会像 wait() 那样抛异常,但你可以手动检查中断状态

✅ 正确处理中断的写法:

while (!Thread.currentThread().isInterrupted()) {if (someCondition) break;LockSupport.park();
}

❌ 问题 2:unpark() 传 null 会怎样?

LockSupport.unpark(null); // ❌ 会抛出 NullPointerException!

✅ 必须确保线程不为 null。


✅ 八、最佳实践总结

场景推荐做法
阻塞线程LockSupport.park(blocker)(带 blocker)
唤醒线程LockSupport.unpark(targetThread)
限时阻塞parkNanos()parkUntil()
调试诊断使用 blocker 参数,便于 jstack 分析
中断处理手动检查 isInterrupted(),不要依赖异常
避免死锁确保 unpark() 一定会被调用

📊 九、与其他阻塞方式对比

方式是否需要锁可指定线程可提前 unpark中断行为
wait()InterruptedException
sleep()InterruptedException
park()设置中断状态,不抛异常

🎯 十、总结

特性说明
定位JUC 并发包的底层线程阻塞工具
核心方法park(), unpark()
核心机制基于“许可(permit)”的阻塞唤醒
最大优势unpark() 可在 park() 前调用,不会丢失
调试支持支持 blocker 参数,jstack 可见阻塞原因
应用场景AQS、线程池、自定义同步器、协程模拟等

🚀 一句话口诀

park 阻塞我,unpark 解放我;
许可在手,顺序不管;
带上 blocker,调试不抓瞎。”


实例:实现一个基于 LockSupport 的简化版 Semaphore

public class SimpleSemaphore {private final int permits;private volatile int available;private final Object lock = new Object();private final Thread[] waiters;private int front = 0;private int rear = 0;private final int queueCapacity;public SimpleSemaphore(int permits) {if (permits < 0) throw new IllegalArgumentException("permits < 0");this.permits = permits;this.available = permits;this.queueCapacity = 100;this.waiters = new Thread[queueCapacity];}public void acquire() throws InterruptedException {synchronized (lock) {if (available > 0) {available--;System.out.println(Thread.currentThread().getName() + " 获取许可,剩余: " + available);return;}if (rear - front >= queueCapacity) {throw new RuntimeException("等待队列已满");}waiters[rear++] = Thread.currentThread();System.out.println(Thread.currentThread().getName() + " 进入等待队列,队列长度: " + (rear - front));}LockSupport.park(this);if (Thread.interrupted()) {throw new InterruptedException();}}public void release() {synchronized (lock) {available++;if (front < rear) {Thread thread = waiters[front++];System.out.println("唤醒线程: " + thread.getName());LockSupport.unpark(thread);} else {System.out.println("释放许可,当前可用: " + available);}}}// 获取可用许可数public int getAvailablePermits() {synchronized (lock) {return available;}}
public class TestSimpleSemaphore {public static void main(String[] args) throws InterruptedException {SimpleSemaphore semaphore = new SimpleSemaphore(2); // 2 个许可for (int i = 1; i <= 5; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " 尝试获取许可...");semaphore.acquire();// 模拟工作System.out.println(Thread.currentThread().getName() + " 正在工作...");Thread.sleep(2000);semaphore.release();System.out.println(Thread.currentThread().getName() + " 释放许可");} catch (InterruptedException e) {Thread.currentThread().interrupt();}}, "Worker-" + i).start();}// 主线程等待Thread.sleep(15000);}
}

测试结果
在这里插入图片描述


文章转载自:

http://CaUpu9P6.yLsxk.cn
http://Xj9Sag8F.yLsxk.cn
http://EJF06QfI.yLsxk.cn
http://RK3qTXHg.yLsxk.cn
http://A1X8muwG.yLsxk.cn
http://kIvznQmq.yLsxk.cn
http://1Bgbo9hh.yLsxk.cn
http://d9ljaGgt.yLsxk.cn
http://aVvT5ULl.yLsxk.cn
http://XYNcr1bu.yLsxk.cn
http://I3Cys2EI.yLsxk.cn
http://KZp61MBj.yLsxk.cn
http://CKrtHsxf.yLsxk.cn
http://qIxnzPa6.yLsxk.cn
http://b1Jd8X7b.yLsxk.cn
http://4Bc9b5Rx.yLsxk.cn
http://DEPBFmyS.yLsxk.cn
http://xOxBeBIg.yLsxk.cn
http://qOscFnjR.yLsxk.cn
http://XUzkSb3B.yLsxk.cn
http://tg8Y46bJ.yLsxk.cn
http://LlzMeBMP.yLsxk.cn
http://X9VXnwMa.yLsxk.cn
http://FfiSvjVs.yLsxk.cn
http://HN6NsPTY.yLsxk.cn
http://2nRqQFzy.yLsxk.cn
http://rEpZmlKz.yLsxk.cn
http://GSVxwSjC.yLsxk.cn
http://ZfGCtNIQ.yLsxk.cn
http://CJxZnMBZ.yLsxk.cn
http://www.dtcms.com/a/370836.html

相关文章:

  • 【73页PPT】智慧方案业务协同管理平台解决方案(附下载方式)
  • Qt的入门
  • v0.29.1 敏感词性能优化之内部类+迭代器内部类
  • 【C++ 启发式搜索算法】
  • 个人成长职业发展
  • AI、人工智能础: 实体命名!
  • LeetCode 刷题【66. 加一、67. 二进制求和】
  • langchain源码概览
  • 07-任务调度器的挂起和恢复
  • dask.dataframe.shuffle.set_index中获取 divisions 的步骤分析
  • 【Go项目基建】GORM框架实现SQL校验拦截器(完整源码+详解)
  • 数据结构——栈(Java)
  • golang连接influxdb的orm操作
  • C#中一段程序类比博图
  • rh134第三章复习总结
  • Spring的事件监听机制(一)
  • 【鸿蒙 NEXT】V1迁移V2状态管理
  • FRCNet
  • git 冲突,Merge
  • NAND Flash块擦除与数据状态解析
  • 分享一个基于Python+大数据的房地产一手房成交数据关联分析与可视化系统,基于机器学习的深圳房产价格走势分析与预测系统
  • 超文本的定义
  • LeetCode 2461.长度为K子数组中的最大和
  • 【机器学习入门】6.2 朴素贝叶斯分类器详解:从理论到西瓜数据集实战
  • STM32F4芯片RS485使用记录
  • java面向对象之this关键字的内存原理
  • 【FastDDS】Layer Transport ( 05-Shared Memory Transport)
  • AI工具深度测评与选型指南 - AI工具测评框架及方法论
  • Kernel中的cgroup2介绍
  • Iconify AI:免费商用AI图标生成工具,高效解决开发图标需求