Java JUC LockSupport概述

LockSupport 工具类 LockSupport 是 rt.jar 包中的一个工具类,它的主要作用就是挂起唤醒线程,并且该类是创建锁和其它同步类的基础。
LockSupport 类和每个使用它的线程都会关联一个许可证,在默认情况下调用 LockSupport 类的方法是不持有许可证的。LockSupport 是使用 Unsafe 类实现的。
下面介绍一下 LockSupport 中的主要方法。
void park() 如果在调用 park()方法之前已经获得到关联的许可证,则调用后会立即返回,否则会被禁止参与线程的调度,也就是被阻塞挂起。
如下代码,最终只会输出“开始”,然后当前线程被挂起,因为在默认情况下调用线程是不持有许可证的。

public static void main(String[] args) throws Exception { System.out.println("开始..."); LockSupport.park(); System.out.println("结束..."); } //结果: //开始...

在其它线程调用 unpark(Thread thread)方法并且将当前阻塞线程作为参数时,调用 park()方法阻塞的线程会被返回。当其它线程调用了阻塞线程的 interrupt()方法,设置中断标志或者线程被虚假唤醒,则阻塞线程也会被返回。
需要注意:
  1. 当调用 park()方法被阻塞的线程被其它线程中断而返回时候,并不会抛出 InterruptedException 异常。
  2. 许可证是一次性的。比如线程 B 连续调用了三次 unpark()方法,当线程 A 调用 park()方法就使用掉这个许可证,如果线程 A 再次调用 park()方法,则进入等待状态。
void unpark(Thread t) 当一个线程调用 unpark()方法时,如果传入的参数 thread 没有持有许可证,则让传入的 thread 持有许可证;如果 thread 之前调用 park()后被挂起,则调用 unpark()时会唤醒该 thread;如果 thread 之前没有调用 park(),则调用 unpark()方法后,再调用 park()方法时会立即返回。
public static void main(String[] args) throws Exception { System.out.println("开始..."); LockSupport.unpark(Thread.currentThread()); LockSupport.park(); System.out.println("结束..."); } //输出结果: //开始... //结束...

看如下代码,加深对 park 和 unpark 的理解。
public static void main(String[] args) throws Exception { Thread t = new Thread(() -> { System.out.println("子线程开始..."); //挂起自己 LockSupport.park(); System.out.println("子线程结束..."); }); t.start(); Thread.sleep(1000); System.out.println("主线程开始调用unpark"); LockSupport.unpark(t); } //输出结果: //子线程开始... //主线程开始调用unpark //子线程结束...

该段代码首先创建了子线程 t,子线程启动后调用 park()方法。由于默认情况下子线程没有持有许可证,则挂起自己。
主线程休眠 1 秒后,调用 unpark()并传入子线程 t,这样做就是为了让子线程获取到许可证,之后子线程中阻塞的 park()就会返回。
void parkNanos(long nanos) 和 park()方法类似,如果已经获取到了许可证则调用该方法后会马上返回。不同的是,如果没有拿到许可证,则调用线程会被挂起 nanos 时间后自动返回。
【Java JUC LockSupport概述】另外 park()方法还支持带有 blocker 参数的方法 void park(Object blocker)方法,当线程在没有持有许可证的情况下调用 park()方法而被阻塞挂起时,这个 blocker 对象会被记录到该线程内部。一般用于用于标识阻塞对象,该对象主要用于问题排查和系统监控。
void parkNanos(Object blocker,long nanos) 该方法相对于上个方法,增加了超时时间。
void parkUntil(Object blocker,long deadline)
public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); }

参数 deadline 的时间单位为 ms,这个方法和 parkNanos(Object blocker,long nanos)的区别是,后者是从当前时间等待 nanos 秒时间,而前者是指定一个时间点,比如 2022-01-19 12:00:00。
总结 与 Object 类的 wait/notify 机制相比,park/unpark 有两个优点:
  • 以 thread 为操作对象更符合阻塞线程的直观定义
  • 操作更精准,可以准确地唤醒某一个线程(notify 随机唤醒一个线程,notifyAll 唤醒所有等待的线程),增加了灵活性

    推荐阅读