版权声明:本文是作者在学习与工作中的总结与笔记,如有内容是您的原创,请评论留下链接地址,我会在文章开头声明。 https://blog.csdn.net/usagoole/article/details/89789276
文章目录
LockSupport
LockSupport
是用来创建锁和其他同步类的基本线程阻塞原语。LockSupport
中的park()
和unpark()
的作用分别是阻塞线程和解除阻塞线程,而且park()
和unpark()
不会遇到Thread.suspend
和Thread.resume
所可能引发的死锁”问题。- 因为
park()
和unpark()
有许可的存在;调用park()
的线程和另一个试图将其unpark()
的线程之间的竞争将保持活性。 unpark
函数为线程提供"许可(permit)",线程调用park()
则等待"许可"。这个有点像信号量,但是这个"许可"是不能叠加的,“许可"是一次性的。比如线程B连续调用了三次unpark()
,当线程A调用park()
就使用掉这个"许可”,如果线程A再次调用park()
,则进入等待状态。- 注意,
unpark()
可以先于park()
调用。比如线程B调用unpark()
,给线程A发了一个"许可",那么当线程A调用park()
时,它发现已经有"许可"了,那么它会马上再继续运行。park/unpark模型真正解耦了线程之间的同步,线程之间不再需要一个Object或者其它变量来存储状态,不再需要关心对方的状态。LockSupport
很类似于二元信号量(只有1个许可证可供使用)
方法
-
方法
// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。 static Object getBlocker(Thread t) // 为了线程调度,禁用当前线程,除非许可可用。 static void park() // 为了线程调度,在许可可用之前禁用当前线程。 static void park(Object blocker) // 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。 static void parkNanos(long nanos) // 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。 static void parkNanos(Object blocker, long nanos) // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。 static void parkUntil(long deadline) // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。 static void parkUntil(Object blocker, long deadline) // 如果给定线程的许可尚不可用,则使其可用。 static void unpark(Thread thread)
案例
-
案例一:运行该代码,可以发现主线程一直处于阻塞状态。因为许可默认是被占用的,调用
park()
时获取不到许可,所以进入阻塞状态。@Test public void testPack1() { LockSupport.park(); //不会打印 System.out.println("block."); }
-
案例二:先释放许可,再获取许可,主线程能够正常终止。LockSupport许可的获取和释放,一般来说是对应的,如果多次unpark,只有一次park也不会出现什么问题,结果是许可处于可用状态。
@Test public void testUnpark() { Thread thread = Thread.currentThread(); // 释放许可 LockSupport.unpark(thread); // 获取许可 LockSupport.park(); System.out.println("b"); } /** * 输出 a b c */ @Test public void testPark3() { Thread thread = Thread.currentThread(); LockSupport.unpark(thread); System.out.println("a"); LockSupport.park(); System.out.println("b"); LockSupport.unpark(thread); LockSupport.park(); System.out.println("c"); }
-
案例三:
LockSupport
是不可重入的,如果一个线程连续2次调用LockSupport.park()
,那么该线程一定会一直阻塞下去。/** * 输出 a b * LockSupport是不可重入的,如果一个线程连续2次调用LockSupport.park(), * 那么该线程一定会一直阻塞下去。 */ @Test public void testPark2() { Thread thread = Thread.currentThread(); LockSupport.unpark(thread); System.out.println("a"); LockSupport.park(); System.out.println("b"); LockSupport.park(); System.out.println("c"); }
-
案例四:线程如果因为调用
park()
而阻塞的话,能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException
。/** * 输出: * after 1 second.count=26787704 * main over * thread over.true * 最终线程会打印出thread over.true。这说明线程如果因为调用park而阻塞的话, * 能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException。 */ @Test public void testInterrupt() { Thread t = new Thread(new Runnable() { private int count = 0; @Override public void run() { long start = System.currentTimeMillis(); long end = 0; while ((end - start) <= 1000) { count++; end = System.currentTimeMillis(); } System.out.println("after 1 second.count=" + count); // 等待或许许可 LockSupport.park(); System.out.println("thread over." + Thread.currentThread().isInterrupted()); } }); t.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 中断线程 t.interrupt(); System.out.println("main over"); }