LockSupport的原理和作用是什么

本篇内容介绍了“LockSupport的原理和作用是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

成都创新互联-专业网站定制、快速模板网站建设、高性价比赤壁网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式赤壁网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖赤壁地区。费用合理售后完善,10多年实体公司更值得信赖。

LockSupport是什么

用于创建锁和其他同步类的基本线程阻塞原语。
使用它,可以用来构建一些特定逻辑的锁。jdk并发包的AQS框架就依赖了它。

LockSupport有什么作用

它提供了线程的阻塞和唤醒。
每当一个线程使用它,那么就可以当作这个线程维持了一个二元信号量,可以比作持有许可证。
调用pack()方法,如果有许可证,那么消耗它并且直接返回,线程继续执行下去。反之,则阻塞。
调用unpack()方法,如果许可证不可用,那么就变成可用,如果是可用,那么就不做其他操作。也就是说,许可证不可以多次累加。
因为有许可证这种中间介质,也就避免了调用线程不推荐的Thread.suspend()Thread.resume()而导致的线程失活的竟态情况。这里我个人的理解是,线程调度的复杂性和延迟。比如说:线程的休眠和暂停并不能够做到即可马上、命令之间的重排等等。并且,park()也额外支持了线程打断和超时功能。
类的文档里面额外提及了一个惯用法,说的是pack()调用会"无理由"返回,从而导致线程莫名的执行下去。那么惯用法就是把pack()逻辑包装在一个循环当中,并且循环退出的条件就是线程等待同步许可的条件。这就相当于一个自旋的优化,只是需要unpack()配合。

一个阻塞线程的简单范例:

public static void main(String[] args) {
    LockSupport.park();
    System.out.println("主线程阻塞,并不会打印当前消息");
}

一个通用的使用范式:

    public static void main(String[] args) throws InterruptedException {
        Thread busThreadJim = new Thread(() -> {

            System.out.println("参数检查...");
            System.out.println("执行业务逻辑...");
            System.out.println("进入状态同步点");
            LockSupport.park("因为可能原因A而阻塞住线程");

            System.out.println("同步点完成,进步业务收尾阶段...");
            System.out.println("进入最后一个同步点结束");
            LockSupport.park("报告这个线程当前的工作程度或者状态xxxx");

            System.out.println("业务逻辑完成退出");
        });
        busThreadJim.start();

        System.out.println("控制线程或者监控线程开始做其他逻辑...");
        Thread.sleep(2000);
        System.out.println("检查工作线程是否阻塞住,为什么:" + LockSupport.getBlocker(busThreadJim));
        LockSupport.unpark(busThreadJim);

        // 如果线程调度慢 LockSupport.getBlocker获取到null
        // 也就是说工作线程还没有阻塞住
        System.out.println("可以通过阻塞对象来传出一些线程工作信息:" + LockSupport.getBlocker(busThreadJim));
        System.out.println("当然共享变量也是可以的");
        LockSupport.unpark(busThreadJim);

        System.out.println("逻辑完成");
    }

多次unpack()也只能消耗一次

    public static void main(String[] args) throws InterruptedException {

        Thread worker = new Thread(() -> {

            System.out.println("执行业务逻辑...");
            System.out.println("进入状态同步点...");

            LockSupport.park();

            System.out.println("同步点完成,进步业务收尾阶段...");

            LockSupport.park();

            System.out.println("最后一个同步点结束");
        });

        worker.start();
        LockSupport.unpark(worker);
        LockSupport.unpark(worker);

        Thread.sleep(2000);

        LockSupport.unpark(worker);
    }

设计代码逻辑的时候,尽量有明确的阻塞条件,然后选择带有时间截至的函数和提供阻塞信息的对象。
理论上来说,应该用不到这个类。但是一旦用到了,估计就是设计很基础的东西,上面肯定有复杂的逻辑。没有阻塞信息到时候逻辑排查是要命的。

与wait/notify机制的区别

  1. wait/notify必须在同步代码块中使用,光这一点就感觉到限制比较大。

  2. wait/notify操作的是对象,然后才能映射到对象所在的线程,思考方式有点饶。

  3. 复杂的逻辑,特别是嵌套,特别窒息。

源码实现关键点

public class LockSupport {
    private LockSupport() {} // 该类无法实例化

    // 所有的功能都代理给内部来实现
    private static final sun.misc.Unsafe UNSAFE;
    
    // 把当前线程阻塞住
    public static void park() {
        UNSAFE.park(false, 0L);
    }

    // 把当前执行线程与一个阻塞对象相关连
    // 这样子关注对象就变成线程而非对象
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }
    
    // 下面就是一系列时间相关函数
    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);
        }
    }

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }

    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }

    // 唤醒目标也是线程 跟阻塞关注点相同
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

    // 返回阻塞对象信息
    // 如果频繁的pack,那么该对象可能一直在变更,它的生命周期会比较短,只是最近一次阻塞的信息
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }
}

值得注意的关注点在于blocker的信息获取,直接操作内存,因为线程已经阻塞住,只能通过这种机制来获取阻塞线程内信息。

“LockSupport的原理和作用是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!


当前名称:LockSupport的原理和作用是什么
文章来源:http://scyanting.com/article/jppdji.html