• iv012-LockSupport


    1.LockSupport是什么?

    • LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
    • LockSupport中的park()和unPark()的作用分别是阻塞线程和解除阻塞线程

    2.线程等待唤醒机制(wait/notify)

    • 1.三种让线程等待和唤醒的方法
      • 方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程。
      • 方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程。
      • 方式3:LockSupport类可以阻塞当前线程以唤醒指定被阻塞的线程。
    • 2.Object类中的wait和notify方法实现线程等待和唤醒
      • 问题1:Object类中的wait,notify,notifyAll用于线程等待和唤醒的方法,都必须在synchronized内部执行(必须用到关键字synchronized)。
      • 问题2:将notify放在wait方法前面,程序无法执行,无法唤醒。
    • 3.Condition接口中的await后signal方法实现线程的等待和唤醒
      会出现同Object类中的wait和notify方法实现线程等待和唤醒一样的两个问题。
    • 4.传统的synchronized和Lock实现等待唤醒通知的约束。
      线程先要获得并持有锁,必须在锁块(synchronized或lock)中。
      必须要先等待后唤醒,线程才能够被唤醒。
    • 5.LockSupport类中的park等待和unpark唤醒
      是什么:通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作。
      官网解释:LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
      LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),permit只有两个值1和0,默认为0。可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。

    方法调用:

    • 阻塞:park()/park(Object blocker),阻塞当前线程/阻塞传入的具体线程。
      调用LockSupport.park()时
    public static void(){
     UNSAFE.park(false,0L);
    }
    

    permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0并返回。

    • 唤醒:unpark(Thread thread),唤醒处于阻塞状态的指定线程。
      调用LockSupport.unpark(thread)时
    public static void unpark(Thread thread){
        if(thread !=null)
        UNSAFE.unpark(thread);
    }
    

    调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,即之前阻塞中的LockSupport.park()方法会立即返回。
    代码:
    正常+无锁块要求
    之前错误的先唤醒后等待LockSupport照样支持

    public class LockSupportDemo {
    
        public static void main(String[] args) {
    
            Thread a = new Thread(() -> {
    
                System.out.println(Thread.currentThread().getName() + "	 come in");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + "	 唤醒");
            }, "a");
    
            a.start();
    
            Thread b = new Thread(() -> {
    
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LockSupport.unpark(a);
                System.out.println(Thread.currentThread().getName() + "	 通知了");
            }, "b");
    
            b.start();
        }
    }
    

    运行结果:

    a	 come in
    b	 通知了
    a	 唤醒
    
    public class LockSupportDemo {
    
        public static void main(String[] args) {
    
            Thread a = new Thread(() -> {
    
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println(Thread.currentThread().getName() + "	 come in");
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + "	 唤醒");
            }, "a");
    
            a.start();
    
            Thread b = new Thread(() -> {
    
    
                LockSupport.unpark(a);
                System.out.println(Thread.currentThread().getName() + "	 通知了");
            }, "b");
    
            b.start();
        }
    }
    

    运行结果:

    b	 通知了
    a	 come in
    a	 唤醒
    

    原理:
    归根结底,LockSupport调用的Unsafe中的native代码
    LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
    LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就加1变成1,调用一次park就会消费permit,也就是将1变成0,同时park立即返回。如果再次调用park会变成阻塞(因为permit为0了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。每个线程都有一个相关的permit,permit最多只有一个,重复调用unpark也不会累积凭证。形象的理解,线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。当调用park方法时,如果有凭证,则会直接消耗掉这个凭证然后正常退出,如果无凭证,就必须阻塞等待凭证可用。而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

  • 相关阅读:
    在阿里写了8年代码后,我才明白这些道理
    2017双11交易系统TMF2.0技术揭秘,实现全链路管理
    加入新公司快速进入状态的心得
    Kibana+ElasticSearch实现索引数据的几种查询方式
    记一次jenkins发生的无法判断字符串前后空格
    ansible-playbook调试
    记一次ansible-playbook jenkins传空格的标量导致删除了服务的主目录
    rabbitmq集群中队列的完整性
    html5分割上传实现超大文件无插件网页上传思路
    html5分割上传实现超大文件无插件网页上传功能
  • 原文地址:https://www.cnblogs.com/everyingo/p/14554853.html
Copyright © 2020-2023  润新知