理解 Java LockSupport 工具

概述

LockSupport 定义一组静态方法,主要分两类:

  • 以 park 开头的方法提供线程阻塞操作
  • unpark 方法提供线程唤醒操作。

基于 sun.misc.Unsafe,park 和 unpark 方法主要是对 UNSAFE 做封装。其中 UNSAFE 是 Unsafe 对象,初始过程如下:

static{
    try{
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        ...
    }
}

park

park 基于 Unsafe.park,Unsafe.park 定义如下:

public native void park(boolean isAbsolute, long time);

具体实现是平台相关的。如果 isAbsolute 为 true,那么 time 以毫秒计;如果 isAbsolute 为 false,那么 time 以纳秒计。 如果 time 为 0L,则是无限期阻塞线程。

基于 Unsafe.park,可以无限期阻塞线程:

public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}

park 方法会阻塞当前线程直到:

  • 其它线程将当前线程作为参数调用 unpark 方法
  • 其它线程中断当前线程
  • 方法无故返回

可以指定最长阻塞时间:

public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}

parkNanos 会阻塞线程最长 nanos 纳秒。当阻塞时间超过 nanos 纳秒,该方法返回。

或者一直阻塞直到某一时间:

public static void parkUntil(Object blocker, long deadline) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(true, deadline);
    setBlocker(t, null);
}

parkUntil 会阻塞线程直到到达 deadline 时间(从 1970 年开始的毫秒数)

上述方法都要求传入一个 blocker,用于标示当前线程正在等待的对象。blocker 对象主要用于问题排查和监控,也可以选择不传入该 blocker 对象。

unpark

Unsafe.unpark 定义如下:

public native void unpark(Object thread);

该操作是“unsafe”的原因是调用者必须确保线程没有被摧毁。

基于 Unsafe.unpark,unpark 实现如下:

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

调用 unpark 方法时,如果线程 thread 阻塞在 park 方法上,那么该线程将会被唤醒;但是如果 thread 没有被阻塞,那么下一次调用 park 方法将保证该线程不会被阻塞。

setBlocker

private static void setBlocker(Thread t, Object arg) {
    // Even though volatile, hotspot doesn't need a write barrier here.
    UNSAFE.putObject(t, parkBlockerOffset, arg);
}

设置线程正在等待的对象

getBlocker

public static Object getBlocker(Thread t) {
    if (t == null)
        throw new NullPointerException();
    return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}

如果有线程正在阻塞,那么返回最近一次调用 park 方法传入的 blocker 对象;如果没有线程被阻塞,那么返回 null。

getBlocker 返回值只是瞬时值,可能下一时刻线程已经被唤醒或者阻塞在不同的 blocker 对象。


Previous post: 理解 Java Semaphore 实现

Next post: 理解 Java 关键字 synchronized