• LockSupport的使用


    参考链接:

    LockSupport使用场景及原理详解
    AQS的引入

    LockSupport的使用

    LockSupport是一个工具类,提供了基本的线程阻塞和唤醒功能,它是创建锁和其他同步组件的基础工具,内部是使用sun.misc.Unsafe类实现的。LockSupport和使用它的线程都会关联一个许可,park方法表示消耗一个许可,调用park方法时,如果许可可用则park方法返回,如果没有许可则一直阻塞直到许可可用。unpark方法表示增加一个许可,多次调用并不会积累许可,因为许可数最大值为1。

    其次,注意:某个线程可以先被unpark(这时,该线程就获得了一个许可),然后这个线程调用LockSupport.park()时,此时,发现有许可可用,则使用此许可而不会阻塞。

    关于LockSupport的补充

    LockSupport会为每个线程设置一个permit值,也就是许可。1代表许可可用,0代表许可不可用。permit的最大值是1,最小值是0,当调用park()方法时,如果permit为1,则减为0,继续执行。如果permit已经为0,则阻塞。同理,unpark()方法会消耗一个许可,如果permit的值为0,则增加为1,代表发放许可,如果permit的值已经为1,则不在增加,也就是多次方法许可,并不会累加。当然这里所讲的permit在LockSupport的代码当中没有体现,需要到HotSpot的源码当中查看,以下为截图供查看:

    1、/share/vm/runtime/park.hpp中许可的定义字段_counter
    在这里插入图片描述

    2、park()的实现(部分截图)
    在这里插入图片描述

    3、unpark()实现
    在这里插入图片描述

    示例:让3个线程按顺序打印ABC

    package com.zzhua;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class TestABC {
    
        private void printA(Thread thread) {
            System.out.println("A");
            LockSupport.unpark(thread);
        }
    
        private void printB(Thread thread) {
            LockSupport.park();
            System.out.println("B");
            LockSupport.unpark(thread);
        }
    
        private void printC() {
            LockSupport.park();
            System.out.println("C");
        }
    
        public static void main(String[] args) {
    
    		/* 无论下面哪个线程先被执行,当B和C未获得它们的许可时,都会被阻塞掉,一直到获得许可。
    		   或者,它们在它们执行顺先前面的线程执行完后,先给了它们许可后,它们再在park时,再消费此许可
    		   这样就可以保证它们的打印顺序
    		   LockSupport的存在目的就是为了替换掉jdk自带的wait-notify等待唤醒机制(它只能结合synchronized使用,并且只能唤醒一个或全部唤醒,不够灵活) */
    		
            TestABC testABC = new TestABC();
    
            Thread tC = new Thread(() -> {
                testABC.printC();
            });
    
            Thread tB = new Thread(() -> {
                testABC.printB(tC);
            });
    
            Thread tA = new Thread(() -> {
                testABC.printA(tB);
            });
    
            tC.start();
            tB.start();
            tA.start();
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    可正常响应中断

    LockSupport.park()方法可以阻塞当前线程,阻塞的过程中,是可以正常响应中断的(如下面例子)。而jdk自带的synchornized当处于阻塞时,是不会响应中断的。

    package com.zzhua;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class TestPark {
    
        public static void main(String[] args) {
    
            Thread mainThread = Thread.currentThread();
    
            new Thread(() -> {
                try {
                    Thread.sleep(3000L);
                    mainThread.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            
            LockSupport.park();
            
            System.out.println("main....");
    
    		// 3s后,正常输出main....
    		// 说明park()方法可正常响应中断
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    jdk自带的synchronized在阻塞期间是不会响应中断的,以下示例,在10s后,正常输出main…

    
    package com.zzhua;
    
    
    public class TestPark {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread mainThread = Thread.currentThread();
    
            new Thread(() -> {
                try {
                    Thread.sleep(3000L);
                    mainThread.interrupt();
                    System.out.println("尝试中断main线程...(但是main线程没反应)");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
            new Thread(() -> {
                try {
                    synchronized (TestPark.class) {
                        Thread.sleep(10000L);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
            Thread.sleep(50L);
    
            System.out.println("要进入synchronized了");
            synchronized (TestPark.class) {
                System.out.println("main....");
            }
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    打断标记与park(细节拉满)

    调用park之前,线程的打断标记状态都是false,但是有的能park住,有的调用park没有作用

    (解释一:调用interrupt()时,其实也隐藏会调用unpark(),就相当于给这个打断的线程颁发了一次许可)。

    (解释二:当isInterrupted中断标记为true的时候,后面的park都不会成功

    package com.zzhua.test32;
    
    import java.util.concurrent.*;
    import java.util.concurrent.locks.LockSupport;
    
    public class Test32 {
        public static void main(String[] args) {
            /*
    		// 线程被打断(就会颁发了一次许可),第一次park,将会park不住
            System.out.println(Thread.currentThread().isInterrupted()); // false
            Thread.currentThread().interrupt();                         // 打断当前主线程
            System.out.println(Thread.currentThread().isInterrupted()); // true
            System.out.println(Thread.interrupted());                   // 返回打断标记状态,并且清除打断标记
            System.out.println(Thread.currentThread().isInterrupted()); // false
            LockSupport.park();                                         // 在清除打断标记的情况下,去park
            System.out.println(123);                                    // 前面没有park住,这里输出123
    
            */
    
            /*
            // 线程被打断(就会颁发了一次许可),第一次park,将会park不住
            System.out.println(Thread.currentThread().isInterrupted()); // false
            Thread.currentThread().interrupt();                         // 打断当前主线程
            System.out.println(Thread.currentThread().isInterrupted()); // true
            LockSupport.park();                                         // 在打断的情况下,去park
            System.out.println(123);                                    // 前面没有park住,这里输出123
            System.out.println(Thread.currentThread().isInterrupted()); // true
            */
    
            /* 因为清除了打断标记,主线程才得以成功park住
            Thread mainThread = Thread.currentThread();
            new Thread(()->{
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mainThread.interrupt();                      // 3s之后,打断主线程
                System.out.println("打断main线程");
            }).start();
            System.out.println("main即将进入park"); 
            LockSupport.park();                              // 主线程park
            System.out.println(mainThread.isInterrupted());  // true
            System.out.println("main恢复运行");
            Thread.interrupted();                            // 返回打断标记状态,并且清除打断标记
            System.out.println(mainThread.isInterrupted());  // false
            LockSupport.park();                              // 主线程成功park
            */
            
            /* 当isInterrupted中断标记为true的时候,park不会成功
            Thread test = new Thread(() -> {
                Thread.currentThread().interrupt();
                LockSupport.park();
                System.out.println("第1次park");//park不住,还会执行
                Thread.interrupted();//为什么aqs要用interrupted():设置isInterrupted为false,aqs用unpark唤醒,后续才能park住,
                LockSupport.park();
                System.out.println("第2次park");
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("通过interrupt唤醒");
                } else {
                    System.out.println("通过unpark唤醒");
                }
                LockSupport.park();//打断标记为true,无法park;false,可以Park
                System.out.println("第3次park");
                LockSupport.park();
                System.out.println("再来一次park试试,park不住呀" + Thread.currentThread().isInterrupted());
            });
    
    
            test.start();
            Thread.sleep(1000);
            // LockSupport.unpark(test);//对应false,可以park的情况,aqs用unpark唤醒后
            test.interrupt();//对应true,无法park的情况,aqs selfInterrupt后,无法再次park住
            
            */
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
  • 相关阅读:
    基于consul的服务注册与消费案例
    我屮艸芔茻,mongo居然可以自动删除数据
    【无标题】
    【电力负荷预测】模拟退火算法结合狮群算法优化Elman神经网络电力负荷预测【含Matlab源码 1454期】
    盘点10个程序员可以接私活的平台和一些建议
    携创教育:自考延期会补考吗?是否还需要重新注册?
    java类加载过程
    SpringMVC常用注解的详细解析
    deequ 2.0.1 anomaly的代码简单分析
    SpringBoot-16-模块开发-首页登陆并权限拦截
  • 原文地址:https://blog.csdn.net/qq_16992475/article/details/128088521