跳到主要内容

05、Java JUC源码分析 - locks-LockSupport

LockSupport通过unsafe提供阻塞和唤醒线程的方法,AQS和其他的lock都会使用到这个基础类。

private LockSupport() {} // Cannot be instantiated.

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long parkBlockerOffset;

static {
    try {
        parkBlockerOffset = unsafe.objectFieldOffset
            (java.lang.Thread.class.getDeclaredField("parkBlocker"));
    } catch (Exception ex) { throw new Error(ex); }
}

每个线程都有一个volatile Object parkBlocker;字段,用于设置阻塞时,是被那个线程阻塞,可用于监控线程。LockSupport也提供设置和获取parkBlocker方法:

private static void setBlocker(Thread t, Object arg) {
    // Even though volatile, hotspot doesn't need a write barrier here.
    unsafe.putObject(t, parkBlockerOffset, arg);
}
public static Object getBlocker(Thread t) {
    if (t == null)
        throw new NullPointerException();
    return unsafe.getObjectVolatile(t, parkBlockerOffset);
}

LockSupport通过底层unsafe提供park,unpark操作。简单点说:底层维护一个二义性的 _counter来保存一个许可,需要注意的是这个许可是一次性的,unpark操作设置该值为1,park操作检查该值是否为1,为1直接返回,不为1,则阻塞。

考虑下面几种情况是为什么:

1、 unpark->park,unpark释放一个许可,park直接返回,不阻塞;

2、 unpark->park->park,unpark释放一个许可,2次park阻塞;

3、 park->unpark->unpark,park阻塞后多次unpark,unpark修改_counter为1,多次调用也无所谓,为什么单独提出这个,是因为在AQS共享模式下,获取锁后,需要通知所有等待锁的线程,会从阻塞队列中删除/唤醒头结点使用for循环,我感觉有可能多次unpark操作(个人想法,不一定完全正确,谁了解的可以解释下);

如果使用wait/notify来实现同步,notify在wait之前执行,wait会一直阻塞,就会出问题了。

带blocker参数和不带blocker参数:

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

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);
    }
}

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

public static void park() {
    unsafe.park(false, 0L);
}

public static void parkNanos(long nanos) {
    if (nanos > 0)
        unsafe.park(false, nanos);
}

public static void parkUntil(long deadline) {
    unsafe.park(true, deadline);
}

LockSupport的javadoc说明在3中情况下回立即返回:

1、 有其他线程调用了本线程的unpark操作;

2、 其他线程中断了本线程;

3、 Thecallspuriously(thatis,fornoreason)returns.;

除了上面3种情况,底层Unsafe的public native void park(boolean flag, long l);下面情况也会唤醒:

1、 flag为true,传入的dealline到了;

2、 flag为false,传入的park时间time到了;

unpark操作唤醒后,线程调度就由各个平台自己控制:

<span style="font-size:18px;">public static void unpark(Thread thread) {
    if (thread != null)
        unsafe.unpark(thread);
}</span>