LockSupprot 用来阻塞和唤醒线程,底层实现依赖于Unsafe类
该类包含一组用于阻塞和唤醒线程的静态方法,这些方法主要是围绕 park 和 unpark 展开
public class LockSupportDemo1 {public static void main(String[] args) {Thread mainThread = Thread.currentThread();// 创建一个线程从1数到1000Thread counterThread = new Thread(() -> {for (int i = 1; i <= 1000; i++) {System.out.println(i);if (i == 500) {// 当数到500时,唤醒主线程LockSupport.unpark(mainThread);}}});counterThread.start();// 主线程调用parkLockSupport.park();System.out.println("Main thread was unparked.");}
}
上面的代码中,当 counterThread 数到 500 时,它会唤醒 mainThread。而 mainThread 在调用 park 方法时会被阻塞,直到被 unpark。
阻塞线程
void park()
:阻塞当前线程,如果调用 unpark 方法或线程被中断,则该线程将变得可运行。请注意,park 不会抛出 InterruptedException,因此线程必须单独检查其中断状态。void park(Object blocker)
:功能同方法 1,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。void parkNanos(long nanos)
:阻塞当前线程一定的纳秒时间,或直到被 unpark 调用,或线程被中断。void parkNanos(Object blocker, long nanos)
:功能同方法 3,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。void parkUntil(long deadline)
:阻塞当前线程直到某个指定的截止时间(以毫秒为单位),或直到被 unpark 调用,或线程被中断。void parkUntil(Object blocker, long deadline)
:功能同方法 5,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。
唤醒线程
void unpark(Thread thread)
:唤醒一个由 park 方法阻塞的线程。如果该线程未被阻塞,那么下一次调用 park 时将立即返回。这允许“先发制人”式的唤醒机制。
Dump 线程
"Dump 线程"通常是指获取线程的当前状态和调用堆栈的详细快照。这可以提供关于线程正在执行什么操作以及线程在代码的哪个部分的重要信息。
下面是线程转储中可能包括的一些信息:
- 线程 ID 和名称:线程的唯一标识符和可读名称。
- 线程状态:线程的当前状态,例如运行(RUNNABLE)、等待(WAITING)、睡眠(TIMED_WAITING)或阻塞(BLOCKED)。
- 调用堆栈:线程的调用堆栈跟踪,显示线程从当前执行点回溯到初始调用的完整方法调用序列。
- 锁信息:如果线程正在等待或持有锁,线程转储通常还包括有关这些锁的信息。
线程转储可以通过各种方式获得,例如使用 Java 的 jstack 工具,或从 Java VisualVM、Java Mission Control 等工具获取。
设计思路
LockSupport 的设计思路是通过许可证来实现的,就像汽车上高速公路,入口处要获取通行卡,出口处要交出通行卡,如果没有通行卡你就无法出站,当然你可以选择补一张通行卡。
LockSupport 会为使用它的线程关联一个许可证(permit)状态,permit 的语义「是否拥有许可」,0 代表否,1 代表是,默认是 0。
LockSupport.unpark
:指定线程关联的 permit 直接更新为 1,如果更新前的permit<1
,唤醒指定线程LockSupport.park
:当前线程关联的 permit 如果>0,直接把 permit 更新为 0,否则阻塞当前线程
- 线程 A 执行
LockSupport.park
,发现 permit 为 0,未持有许可证,阻塞线程 A - 线程 B 执行
LockSupport.unpark
(入参线程 A),为 A 线程设置许可证,permit 更新为 1,唤醒线程 A - 线程 B 流程结束
- 线程 A 被唤醒,发现 permit 为 1,消费许可证,permit 更新为 0
- 线程 A 执行临界区
- 线程 A 流程结束
经过上面的分析得出结论 unpark 的语义明确为「使线程持有许可证」,park 的语义明确为「消费线程持有的许可」,所以 unpark 与 park 的执行顺序没有强制要求,只要控制好使用的线程即可,unpark=>park
执行流程如下
- permit 默认是 0,线程 A 执行 LockSupport.unpark,permit 更新为 1,线程 A 持有许可证
- 线程 A 执行 LockSupport.park,此时 permit 是 1,消费许可证,permit 更新为 0
- 执行临界区
- 流程结束