Java多线程并发(5)LockSupport

92 阅读2分钟

LockSupport

LockSupport类是用来对线程进行同步的工具类,其作用是阻塞、唤醒线程。其中没有对外构造方法。其中所有的方法都是静态的,直接使用。

在Java基础里学过 Object().wait()和notify()方法可以阻塞、唤醒线程,JUC中的 Condition.await()\signal()也可以实现。

而在LockSupport中有 park()和unpark(Thread thread)实现阻塞、唤醒。

wait()\notify()

这两个方法,在使用的时候必须包裹在同步代码块中,否则直接抛出异常,并且两个方法的执行顺序必须固定且成对出现,否则会出现线程永久休眠的状况。

这两个方法,在使用的时候会出现两个缺陷:

两个方法必须被同步代码块包裹。否则直接抛出异常。

两个方法的执行顺序必须是wait()notify()先执行,否则会使得线程永久休眠,同时必须成对使用。

await()\signal()

他们有和上一对方法相同的缺陷。

park()\unpark(Thread thread)

对于 unpark(Thread thread)来说,它可以唤醒一个指定的线程,如果线程不是阻塞状态,那么他会颁发一个“许可证”,对每一个线程来说,最高一个许可证,而在执行 park()的时候,对于没有许可证的线程来说,他会直接阻塞它,对有许可证的来说,他会消耗这张许可证。

Thread thread = new Thread(() ->{
    for(int i = 0;i < 1000000;i++){
    }
    LockSupport.park();
    System.out.println(1);
});
thread.start();
new Thread(() -> {
    System.out.println(2);
    LockSupport.unpark(thread);
}).start();

这里的代码,会输出2,1。

说明使用LockSupport API他并不会有之前两种方法的缺陷。

源码解析

这里我们来看看park()的源码。在java中,LockSupport调用的是一个同名本地方法,于是我们到openJDK8下的本地方法源码查看:

JNIEXPORT void JNICALL
Java_java_util_concurrent_locks_LockSupport_park(JNIEnv *env, jclass clazz, jobject blocker)
{
    intptr_t park_blocker = (intptr_t)blocker;
    (void)env; (void)clazz;
    THREAD_LOCK();
    threadnode_t *self_node = THREAD_SELF_NODE();
    self_node->park_blocker = park_blocker;

    while (1) {
        /* check if not interrupted */
        if (!self_node->interrupted && self_node->unparked == 0) {
            THREAD_WAIT(self_node->thread_lock);
            continue;
        }
        self_node->interrupted = 0;
        self_node->unparked = 0;
        THREAD_UNLOCK();
        return;
    }
}

我们看向while循环中:

它会判断线程是否处于中断,如果是那么会退出判断并清空中断标志和许可证

如果否则继续判断是否有许可证,然后让线程继续阻塞。

仔细看他在使用while判断的前后加上了THREAD_LOCK();THREAD_UNLOCK();,这就是park()不需要加锁的原因了。