• LockSupport浅析


    最初想有没有必要写这类文章,网上相关的文章很多,有些更为透彻,自己再写一篇不免有重复造轮子的感觉。
    但想想写文除了分享知识外也可以帮助自己总结归纳,也稍稍可以提高点自我满足感。


    基本的线程阻塞原语,被用于创建锁和其他同步类上。

    这个类的作用有点类似于Semaphore,通过许可证(permit)来联系使用它的线程。如果许可证可用,调用park方法会立即返回并在这个过程中消费这个许可,不然线程会阻塞。调用unpark会使许可证可用。(和Semaphores有些许区别,许可证不会累加,最多只有一张)

    因为有了许可证,所以调用parkunpark的先后关系就不重要了,这里可以对比一下Object的waitnotify,如果先调用同一个对象的notifywait,那么调用wait的线程依旧会被阻塞,依赖方法的调用顺序。在一些场景下,LockSupport的方法都可以取代notifywait。举个简单的例子:

    final Object lock = new Object();
    Thread t1 = new Thread() {
        public void run() {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock) {
                lock.notifyAll();
            }
        };
    };
    
    Thread t2 = new Thread() {
        public void run() {
            synchronized (lock) {
                try {
                    lock.wait();
                    System.out.println("I am alive");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
    };
    t1.start();
    t2.start();
    

    这段代码,只有确保t2先进入临界区并执行wait后才能正常打印内容。
    换成LockSupport就没有这种担心了。

    Thread t2 = new Thread() {
        public void run() {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 end sleep");
            LockSupport.park(this);
            System.out.println("I am alive");
        };
    };
    
    Thread t1 = new Thread() {
        public void run() {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LockSupport.unpark(t2);
            System.out.println("I am done");
        };
    };
    t1.start();
    t2.start();
    

    可以先unparkpark,但如API文档所说,unpark需要传入的线程已经启动。在上述t1.start()后用t1.join()等方法在t2启动前调用了unpark就无效了。

    但也不意味着wait,notify就没用了。wait是调用对象的方法,和这个对象资源绑定,线程间不需要彼此感知到对方(能获取对方引用),只需要共有这个资源即可,另一方面LockSupport可以指定unpark的线程,这点也是前者无法做到的,两者间不能完全取代彼此。

    park方法也有可能在没有"任何理由"的情况下返回,所以通常要在一个循环中使用它并重新判断可以返回的条件。这一点和Object.wait方法一样,可以参考wiki,大致可以理解为底层系统因为优化需要允许出现这样的情况,JVM的线程就是系统线程这种情况也无法避免。可以作为"忙等待"的优化,但是必须要注意要有配对的unpark方法。
    使用模式如下:

    while (!canProceed()) { ... LockSupport.park(this); }}  
    

    在提供的方法中可以看到有一组传入名为blocker对象作为参数的方法,记录这个对象可以方便许可证监视和诊断工具分析。

    取上述代码,不启动t1的情况直接运行t2进入阻塞,jstack可以看到:

    "Thread-0" #10 prio=5 os_prio=0 tid=0x000000001b586800 nid=0x37f0 waiting on condition [0x000000001c0ae000]
    java.lang.Thread.State: WAITING (parking)
            at sun.misc.Unsafe.park(Native Method)
            - parking to wait for  <0x00000000d603a6e8> (a test.TestLockSupport$1)
            at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
            at test.TestLockSupport$1.run(TestLockSupport.java:21)  
    

    会将传入的对象打出。


    更多资料:
    https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/LockSupport.html

    http://agapple.iteye.com/blog/970055

    http://blog.csdn.net/hengyunabc/article/details/28126139

  • 相关阅读:
    Anaconda3的Jupyter notebook调用ArcGISPro的Arcpy
    ArcMap 创建空间邻接矩阵
    Anaconda3的Jupyter notebook切换Python3和Python2环境并调用Arcpy
    PHP.MVC的模板标签系统(二)
    PHP汉字转拼音的类
    Linux 解压命令大全
    ASP应用之模板采用
    PHP中的串行化变量和序列化对象(一)
    CSS常用技巧介绍
    ASP实现多图片上传(一)
  • 原文地址:https://www.cnblogs.com/fairjm/p/locksuport.html
Copyright © 2020-2023  润新知