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

原文地址:https://www.cnblogs.com/FondWang/p/12112251.html