浅谈LockSupport工具类
作者:互联网
引
Java的并发包是基于AQS (AbstractQueuedSynchronizer)框架的,AQS框架需要借助于两个工具类:
- Unsafe(提供CAS操作)
- LockSupport(提供park/unpark操作)
LockSupport的使用
LockSupport定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。
在Java6中,LockSupport增加了三个带有blocker的方法。参数blocker用来指出当前线程在等待的对象,即阻塞对象,该对象主要用于问题排查和系统监控。
public static void park(Object blocker); // 暂停当前线程
public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制
public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间
现在通过具体代码看看是如何使用的
LockSupport的原理(以park和unpark为例说明)
1. 源码分析
归根结底,LockSupport.park()和LockSupport.unpark(Thread thread)调用的是Unsafe类中的native代码:
//LockSupport中
public static void park() {
UNSAFE.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
Unsafe类中的对应方法:
//park
public native void park(boolean isAbsolute, long time);
//unpack
public native void unpark(Object var1);
Unsafe.park()和Unsafe.unpark()的底层,在Linux系统下,是用的POSIX的线程库pthread中的mutex(互斥量),condition(条件变量)来实现的,即互斥锁+条件变量。mutex和condition保护了一个_counter的变量,当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。
2. 特性和思想
2.1 许可
LockSupport类使用了一种名为permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量,但这个许可的累加上限是1。初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。也就是说,park/unpark的设计原理核心是“许可”:park是等待一个许可,unpark是为某线程提供一个许可。如果某线程A调用park,那么除非另外一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上。
但是这个“许可”是不能叠加的,“许可”是一次性的。比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。
2.2 顺序
park和unpark的先后顺序并不是那么严格,即unpark操作可以再park操作之前。这时候规则是这样的:
- unpark调用时,如果当前线程还未进入park,则许可为true
- park调用时,判断许可是否为true,如果是true,则继续往下执行;如果是false,则等待,直到许可为true
LockSupport和wait/notify的区别
- LockSupport以线程为操作对象更符合阻塞线程的直观定义。
- 操作更精准,可以准确地唤醒某一个线程,增加了灵活性。而notify随机唤醒一个线程,notifyAll唤醒所有等待的线程。
- wait和notify都是Object中的方法,在调用这两个方法前必须先获得对象的锁,但是park不需要获取某个对象的锁就可以锁住线程。
参考资料
Java多线程进阶(五)—— J.U.C之locks框架:LockSupport
标签:调用,浅谈,park,LockSupport,unpark,线程,工具,许可 来源: https://blog.csdn.net/qq_42052956/article/details/112389030