• 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() 时,发现已经有 " 许可 ",可以马上继续运行,不会阻塞。
    //阻塞线程
    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() 方法的逻辑。
    • 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。



  • 相关阅读:
    c#的逆向工程-IL指令集
    利用nginx concat模块合并js css
    DotNetOpenAuth实践之Webform资源服务器配置
    STL使用迭代器逆向删除
    驱动安装时的错误
    How to detect the presence of the Visual C++ 2010 redistributable package
    Shell脚本使用汇总整理——文件夹及子文件备份脚本
    Shell脚本使用汇总整理
    Shell脚本使用汇总整理——mysql数据库5.7.8以后备份脚本
    Shell脚本使用汇总整理——mysql数据库5.7.8以前备份脚本
  • 原文地址:https://www.cnblogs.com/youngao/p/12573306.html
Copyright © 2020-2023  润新知