JUC中的LockSupport工具类的使用下篇
上篇我们讨论过Object和Condtion来控制线程,但是它们有局限性。现在我们来介绍LockSupport
LockSupport类介绍
LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。主要是通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作的。
每个线程都有一个许可(permit),permit只有两个值1和0,默认是0。
当调用unpark(thread)方法,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)。
当调用park()方法,如果当前线程的permit是1,那么将permit设置为0,并立即返回。如果当前线程的permit是0,那么当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0,并返回。
注意:因为permit默认是0,所以一开始调用park()方法,线程必定会被阻塞。调用unpark(thread)方法后,会自动唤醒thread线程,即park方法立即返回。
LockSupport中常用的方法
- park():阻塞当前线程,如果调用unpark方法或者当前线程被中断,从能从park()方法中返回
- park(Object blocker):功能同方法1,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查
- parkNanos(long nanos):阻塞当前线程,最长不超过nanos纳秒,增加了超时返回的特性
- parkNanos(Object blocker, long nanos):功能同方法3,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查
- parkUntil(long deadline):阻塞当前线程,直到deadline,deadline是一个绝对时间,表示某个时间的毫秒格式
- parkUntil(Object blocker, long deadline):功能同方法5,入参增加一个Object对象,用来记录导致线程阻塞的阻塞对象,方便进行问题排查
唤醒线程代码如下:
public static void testLockSupport() throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("start=====================>" + Thread.currentThread().getName());
LockSupport.park();
System.out.println("end==========================>" + Thread.currentThread().getName());
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(5);
LockSupport.unpark(t1);
System.out.println("unpark=======================> end");
}
t1中调用 LockSupport.park();让当前线程t1等待,主线程休眠了5秒之后,调用 LockSupport.unpark(t1);将t1线程唤醒,输出结果中1、3行结果相差5秒左右,说明t1线程等待5秒之后,被唤醒了。
LockSupport.park();无参数,内部直接会让当前线程处于等待中;unpark方法传递了一个线程对象作为参数,表示将对应的线程唤醒。
其实这两个方法没有前后关系的。如下代码
public static void testLockSupportAfter() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("start=====================>" + Thread.currentThread().getName());
LockSupport.park();
System.out.println("end==========================>" + Thread.currentThread().getName());
}, "t1");
t1.start();
TimeUnit.SECONDS.sleep(1);
//先执行unpark方法
LockSupport.unpark(t1);
System.out.println("unpark=======================> end");
}
代码中启动t1线程,t1线程内部休眠了5秒,然后主线程休眠1秒之后,调用了 LockSupport.unpark(t1);唤醒线程t1,此时 LockSupport.park();方法还未执行,说明唤醒方法在等待方法之前执行的;输出结果中2、3行结果时间一样,表示 LockSupport.park();没有阻塞了,是立即返回的。
说明:唤醒方法在等待方法之前执行,线程也能够被唤醒,这点是另外2中方法无法做到的。Object和Condition中的唤醒必须在等待之后调用,线程才能被唤醒。而LockSupport中,唤醒的方法不管是在等待之前还是在等待之后调用,线程都能够被唤醒。
线程等待和唤醒的3种方式做个对比
方式1:Object中的wait、notify、notifyAll方法
方式2:juc中Condition接口提供的await、signal、signalAll方法
方式3:juc中的LockSupport提供的park、unpark方法