当需要阻塞或者唤醒一个线程的时候,AQS 都是使用 LockSupport 这个工具类来完成的。

LockSupport 是用来创建锁和其他同步类的基本线程阻塞原语。

每个使用 LockSupport 的线程都会与一个许可与之关联:

  • 如果该许可可用,并且可在进程中使用,则调用 #park(…) 将会立即返回,否则可能阻塞。
  • 如果许可尚不可用,则可以调用 #unpark(…) 使其可用。
  • 但是,注意许可不可重入,也就是说只能调用一次 park(…) 方法,否则会一直阻塞。

LockSupport 定义了一系列以 park 开头的方法来阻塞当前线程,unpark(Thread thread) 方法来唤醒一个被阻塞的线程。如下图所示:
image.png

  • park(Object blocker) 方法的blocker参数,主要是用来标识当前线程在等待的对象,该对象主要用于问题排查和系统监控
  • park 方法和 unpark(Thread thread) 方法,都是成对出现的。同时 unpark(Thread thread) 方法,必须要在 park 方法执行之后执行。当然,并不是说没有调用 unpark(Thread thread) 方法的线程就会一直阻塞,park 有一个方法,它是带了时间戳的 #parkNanos(long nanos) 方法:为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。

    park

    1. public static void park() {
    2. UNSAFE.park(false, 0L);
    3. }

    unpark

    1. public static void unpark(Thread thread) {
    2. if (thread != null)
    3. UNSAFE.unpark(thread);
    4. }

    实现原理

    从上面可以看出,其内部的实现都是通过 sun.misc.Unsafe 来实现的,其定义如下:
    1. // UNSAFE.java
    2. public native void park(boolean var1, long var2);
    3. public native void unpark(Object var1);
    两个都是 native 本地方法。Unsafe 是一个比较危险的类,主要是用于执行低级别、不安全的方法集合。尽管这个类和所有的方法都是公开的(使用 public 进行修饰),但是这个类的使用仍然受限,你无法在自己的 Java 程序中直接使用该类,因为只有授信的代码才能获得该类的实例。

Thread.sleep()和LockSupport.park()的区别

LockSupport.park()还有几个兄弟方法——parkNanos()、parkUtil()等,我们这里说的park()方法统称这一类方法。
(1)从功能上来说,Thread.sleep()和LockSupport.park()方法类似,都是阻塞当前线程的执行,且都不会释放当前线程占有的锁资源
(2)Thread.sleep()没法从外部唤醒,只能自己醒过来;
(3)LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒;
(4)Thread.sleep()方法声明上抛出了InterruptedException中断异常,所以调用者需要捕获这个异常或者再抛出;
(5)LockSupport.park()方法不需要捕获中断异常;
(6)Thread.sleep()本身就是一个native方法;
(7)LockSupport.park()底层是调用的Unsafe的native方法;

Object.wait()和LockSupport.park()的区别

二者都会阻塞当前线程的运行,他们有什么区别呢?经过上面的分析相信你一定很清楚了,真的吗?往下看!
(1)Object.wait()方法需要在synchronized块中执行;
(2)LockSupport.park()可以在任意地方执行;
(3)Object.wait()方法声明抛出了中断异常,调用者需要捕获或者再抛出;
(4)LockSupport.park()不需要捕获中断异常【本文由公从号“彤哥读源码”原创】;
(5)Object.wait()不带超时的,需要另一个线程执行notify()来唤醒,但不一定继续执行后续内容;
(6)LockSupport.park()不带超时的,需要另一个线程执行unpark()来唤醒,一定会继续执行后续内容;
(7)如果在wait()之前执行了notify()会怎样?抛出IllegalMonitorStateException异常
(8)如果在park()之前执行了unpark()会怎样?线程不会被阻塞,直接跳过park(),继续执行后续内容;