LockSupport类

一、LockSupport的介绍

LockSupport,构建同步组件的基础工具,帮AQS完成相应线程的阻塞或者唤醒的工作。LockSupport所有的方法都是静态方法,可以让线程在任意位置阻塞。这个类与每个使用它的线程相关联一个许可证(最多一个),如果许可证可用,则在此过程中消耗它; 否则线程会被等待。

二、LockSupport的常用方法

LockSupport类的主要方法如下:

public static void park(Object blocker); // 暂停当前线程
public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制,
public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间
public static void park(); // 无期限暂停当前线程
public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制,时间是纳秒!,不是毫秒!,更不是秒!
public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间
public static void unpark(Thread thread); // 恢复线程 thread
public static Object getBlocker(Thread t);

Object blocker其实就是方便在线程dump的时候看到具体的阻塞对象的信息。

三、LockSupport的原理

每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex 打个比喻

  • 线程就像一个旅人,Parker 就像他随身携带的背包,条件变量就好比背包中的帐篷。_counter 就好比背包中的备用干粮(0 为耗尽,1 为充足,最多只能为1)
  • 调用 park 就是要看需不需要停下来歇息
    • 如果备用干粮耗尽(_counter =0),那么钻进帐篷歇息(线程暂停)
    • 如果备用干粮充足,那么不需停留,继续前进
  • 调用 unpark,就好比令干粮充足
    • 如果这时线程还在帐篷(处于暂停状态),就唤醒让他继续前进(恢复线程)
    • 如果这时线程还在运行,那么下次他调用 park 时,仅是消耗掉备用干粮,不需停留继续前进

值得注意的是:_counter最多只能为1;

图解:

park线程

image-20201009145432328

  1. 当前线程调用 Unsafe.park() 方法

  2. 检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁

  3. 线程进入 _cond 条件变量阻塞

  4. 设置 _counter = 0

unpark唤醒park的线程:

image-20201009150405240

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1

  2. 唤醒 _cond 条件变量中的 Thread_0

  3. Thread_0 恢复运行

  4. 设置 _counter 为 0

先unpark再park:

image-20201009150659559

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1

  2. 当前线程调用 Unsafe.park() 方法

  3. 检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行

  4. 设置 _counter 为 0

四、代码测试

测试一下先unpark在park的情况:

public class LockSupportTest{
public static void main(String[] args) throws InterruptedException {
     
        test4();
    }

    private static void test4() throws InterruptedException {
        final Object obj = new Object();
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
			  System.out.println("park...");
            //禁用当前线程进行线程调度,除非时间过了,或者interrupt或者unpark();
            synchronized (obj) {
                LockSupport.park(1000l);

                }
                System.out.println("unpark");
        });
        t1.start();
        LockSupport.unpark(t1);
        System.out.println("先unpark");
    }
}

结果:线程先unpark,_counter 会保存一个信号量(最多只能一个),等到下一次park的时候线程就可以直接运行不必暂停.

五、总结:

image-20201009152102148

图片来自博客https://blog.csdn.net/u013332124/article/details/84647915

posted @ 2020-10-09 15:40  下海搬砖  阅读(157)  评论(0编辑  收藏  举报