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()不需要加锁的原因了。