LockSupport 工具相关整理
Posted youngao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LockSupport 工具相关整理相关的知识,希望对你有一定的参考价值。
1. LockSupport
- LockSupport 是用来创建锁和其他同步类的基本线程阻塞原语。
- 是一个简单的代理类,里面的代码都是使用 Unsafe 类里面的方法。
- JDK 对 LockSupport 的描述:Basic thread blocking primitives for creating locks and other synchronization classes。
- 在 Java 多线程中,当需要阻塞或者唤醒一个线程时,都会使用 LockSupport 工具类来完成相应的工作。
- LockSupport 定义了一组公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而 LockSupport 也因此成为了构建同步组件的基础工具。
- LockSupport 中的
park()
和unpark()
的作用分别是阻塞线程和解除阻塞线程,而且park()
和unpark()
不会遇到 " Thread.suspend " 和 " Thread.resume " 所可能引发的 死锁 问题。- 因为
park()
和unpark()
有许可的存在。 - 调用
park()
的线程和另一个试图将其unpark()
的线程之间的竞争将保持活性。
- 因为
1.1 LockSupport 函数列表
// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)
- LockSupport 的核心方法是基于 Unsafe 类中的
park()
和unpark()
方法。- 变量 isAbsolute 代表传入的 time 是绝对时间还是相对时间。
- unpark 函数为线程提供 " 许可(permit)",线程调用
park()
函数则等待 " 许可 ",有点像信号量,但是这个 " 许可 " 是不能叠加的," 许可 " 是一次性的。可以理解为设置一个变量 0,1 之间的切换。 - 如果线程 B 连续调用了多次
unpark()
函数,当线程 A 调用park()
函数就使用了这个 " 许可 ",如果线程 A 第二次调用park()
,则进入等待状态。 unpark()
函数可以先于park()
调用。- 如线程 B 调用
unpark()
函数,给线程 A 一个 " 许可 ",那么当线程 A 调用park()
时,发现已经有 " 许可 ",可以马上继续运行,不会阻塞。
- 如线程 B 调用
//阻塞线程
public native void park(boolean isAbsolute, long time);
//取消阻塞线程
public native void unpark(Object thread);
- 调用了
park()
方法后,会禁用当前线程,以下几种情况时,线程会被唤醒。- 其他某个线程将当前线程作为目标调用
unpark()
。(调用 unpark 方法) - 其他某个线程中断当前线程。(被中断 interrupts)
- 该调用不合逻辑地(即毫无理由地)返回。(posix condition 里的 " Spurious wakeup ")
- 其他某个线程将当前线程作为目标调用
public static void park(Object blocker) {
//获取当前线程
Thread t = Thread.currentThread();
//设置线程的blocker对象
setBlocker(t, blocker);
//通过UNSAFE调用,挂起线程
UNSAFE.park(false, 0L);
//挂起的线程被唤醒以后,需要将阻塞的Blocker清理掉。
setBlocker(t, null);
}
-
获取当前线程,设置当前线程的 parkBlocker 字段,调用 Unsafe 类的
park()
方法,再次调用setBlocker 的原因。- Unsafe 的
park()
方法之后,当前线程已被阻塞。 unpark()
方法被调用,该线程获得许可后,继续进行下面的代码,setBlocker()
参数 parkBlocker 字段设置为 null,这样就完成了整个park()
方法的逻辑。
- Unsafe 的
-
setBlocker 修改的是 parkBlockerOffset 变量。
- 这个变量是挂起线程对象的偏移地址,对应的是 Thread 类的 parkBlocker。
- 这个对象是用于记录线程被阻塞时被谁阻塞,用于线程监控和分析工具定位原因。
- parkBlocker 是在线程处于阻塞的情况下才被赋值。线程已经被阻塞,只能通过设置偏移量这种修改内存的方法来进行修改,如果调用线程内的方法,线程是不会回应调用的。
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));
- 阻塞当前线程,最长等待时间不超过 nanos 毫秒,同样,在阻塞当前线程的时候做了记录当前线程等待的对象操作。
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);
}
}
- 阻塞当前线程直到 deadline 时间,相同的,也做了阻塞前记录当前线程等待对象的操作。
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
- 线程在
park()
上受阻塞,将解除其阻塞状态。- 否则,预发许可,下一次调用
park()
不会受阻塞。
- 否则,预发许可,下一次调用
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
1.2 与 wait()、notify() 的区别
- 面向的主体不一样。
- LockSupport 主要是针对 Thread 进进行阻塞处理,可以指定阻塞队列的目标对象,每次可以指定具体的线程唤醒。
- Object.wait() 是以对象为纬度,阻塞当前的线程和唤醒单个(随机)或者所有线程。
- 实现机制不同。
- 虽然 LockSupport 可以指定 Monitor 的 object 对象,但和 object.wait() 两者的阻塞队列并不交叉。
- LockSupport 阻塞和解除阻塞线程直接操作的是 Thread。而 Object 的 wait/notify 并不是直接对线程操作,是被动的方法,需要一个 Object 来进行线程的挂起或唤醒。
- Thead 在调用 wait 之前,当前线程必须先获得该对象的监视器(syschronized),被唤醒之后需要重新获取到监视器才能继续执行。而 LockSupport 可以随意进行 park 或者 unpark。
作者:羽杰
链接:https://www.jianshu.com/p/2a79c115464c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
以上是关于LockSupport 工具相关整理的主要内容,如果未能解决你的问题,请参考以下文章