一、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线程
-
当前线程调用 Unsafe.park() 方法
-
检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
-
线程进入 _cond 条件变量阻塞
-
设置 _counter = 0
unpark唤醒park的线程:
-
调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
-
唤醒 _cond 条件变量中的 Thread_0
-
Thread_0 恢复运行
-
设置 _counter 为 0
先unpark再park:
-
调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
-
当前线程调用 Unsafe.park() 方法
-
检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
-
设置 _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的时候线程就可以直接运行不必暂停.
五、总结:
图片来自博客https://blog.csdn.net/u013332124/article/details/84647915