JUC之LockSupport构建同步组件的基本工具
Posted fondwang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC之LockSupport构建同步组件的基本工具相关的知识,希望对你有一定的参考价值。
一、前言
LockSupport工具类用于阻塞或唤醒线程。LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程组阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。
LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。
二、源码分析
2.1 属性
public class LockSupport {
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
// 表示内存偏移地址
private static final long parkBlockerOffset;
// 表示内存偏移地址
private static final long SEED;
// 表示内存偏移地址
private static final long PROBE;
// 表示内存偏移地址
private static final long SECONDARY;
static {
try {
// 获取Unsafe实例
UNSAFE = sun.misc.Unsafe.getUnsafe();
// 线程类类型
Class<?> tk = Thread.class;
// 获取Thread的parkBlocker字段的内存偏移地址
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
// 获取Thread的threadLocalRandomSeed字段的内存偏移地址
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
// 获取Thread的threadLocalRandomProbe字段的内存偏移地址
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
// 获取Thread的threadLocalRandomSecondarySeed字段的内存偏移地址
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
2.2 构造器
// 私有构造函数,无法被实例化
private LockSupport() {}
2.3 方法
LockSupport方法都是基于Unsafe类中定义的park和unpark方法
① park方法
阻塞当前线程,如果调用unpark(Thread thread) 方法或者当前线程被中断,才能从park() 方法返回
//获取许可,设置时间无限长,直到可以获取许可
public static void park() {
UNSAFE.park(false, 0L);//调用本地方法park
}
public static void park(Object blocker) {
Thread t = Thread.currentThread(); //当前线程
setBlocker(t, blocker); //设置Blocker
UNSAFE.park(false, 0L); //调用本地方法park
setBlocker(t, null); //重新可运行后再次设置Blocker
}
② parkNanos方法
阻塞当前线程,最长不超过nanos纳秒,返回条件在park() 的基础上增加了超时返回
public static void parkNanos(long nanos) {
if (nanos > 0) //时间要大于0
UNSAFE.park(false, nanos); //给定时间阻塞
}
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) { //时间大于0
Thread t = Thread.currentThread(); //当前线程
setBlocker(t, blocker); //设置Blocker
UNSAFE.park(false, nanos); //设置指定时间阻塞
setBlocker(t, null); //设置Blocker
}
}
③ parkUntil方法
阻塞当前线程,知道deadline时间(从1970年开始到deadline时间的毫秒数)
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
上面其中三个方法中,都连续调用链setBlocker() ,为什么呢?
1. 调用park函数时,当前线程首先设置好parkBlocker字段,然后再调用Unsafe的park函数,此后,当前线程就已经阻塞了;
2. 等待该线程的unpark函数被调用,所以后面的一个setBlocker函数无法运行,unpark函数被调用,该线程获得许可后,就可以继续运行了;
3. 也就运行第二个setBlocker,把该线程的parkBlocker字段设置为null,这样就完成了整个park函数的逻辑。
如果没有第二个setBlocker,那么之后没有调用park(Object blocker),而直接调用getBlocker函数,得到的还是前一个park(Object blocker)设置的blocker,显然是不符合逻辑的。总之,必须要保证在park(Object blocker)整个函数执行完后,该线程的parkBlocker字段又恢复为null。
④ unpark方法
唤醒处于阻塞状态的线程thread
public static void unpark(Thread thread) {
if (thread != null) // 线程为不空
UNSAFE.unpark(thread); // 释放该线程许可
}
⑤ setBlocker() 和 getBlocker()
// 设置当前线程阻塞的原因,可以方便调试(线程在哪个对象上阻塞了)
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);
}
感谢:https://www.cnblogs.com/leesf456/p/5347293.html
以上是关于JUC之LockSupport构建同步组件的基本工具的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点# JUC锁: LockSupport详解
Java多线程系列--“JUC锁”07之 LockSupport