• 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

  • 相关阅读:
    推荐一个c++小巧开源且跨平台的图像解码库
    设计模式---桥接模式
    redis数据结构及其使用场景、持久化、缓存淘汰策略
    mysql------explain工具
    mysql索引数据结构
    java8(2)--- Stream API
    java8(1)--- lambda
    springboot自动装配(2)---实现一个自定义自动装配组件
    springboot自动装配(1)---@SpringBootApplication注解怎么自动装配各种组件
    自己挖的坑跪着也要填完---mapper配置文件和java源文件在同一包下
  • 原文地址:https://www.cnblogs.com/FondWang/p/12112251.html
Copyright © 2020-2023  润新知