一、前言
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
原文地址:https://www.cnblogs.com/FondWang/p/12112251.html