并发包中的LockSupport工具类
一、 LockSupport简介
LockSupport类是一个Java6(JSR166-JUC)引入的一个工具类,它的主要作用是对线程进行挂起和唤醒,它是创建锁和其他同步类的基础。
LockSupport类的核心方法其实就两个:park()和unpark(),其中park()方法用来阻塞当前调用线程,unpark()方法用于唤醒指定线程。这和Object的wait/notify、Condition的await/signal、以及Semaphore的acquire/release功能类似。
但是LockSupport的这两种方法从语意上讲比Object类的方法更清晰,而且可以针对指定线程进行阻塞和唤醒。
注意:park方法真正的实现是Unsafe类
private static final sun.misc.Unsafe UNSAFE;
public static void park() {
UNSAFE.park(false, 0L);
}
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。
初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。
二、 LockSupport API
Park方法:
如果调用LockSupport.park()方法的线程已经拿到了与LockSupport关联的许可证,则调用时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
public class LockSupportMain {
public static void main(String[] args) {
System.out.println("main线程----------park前");
//直接在main函数中调用park()方法,当前线程被挂起。
//在默认情况下,调用线程是不持有许可证的。
LockSupport.park();
System.out.println("main线程----------park后");
}
}
****************************只会输出调用park()方法前内容*******
main线程----------park前
unPark(Thread thread):
当一个线程调用LockSupport.unpark()方法时,如果参数thread线程没有持有thread与LockSupport类关联的许可证,则让thread线程持有;
而如果thread之前因调用LockSupport.park()方法时被挂起,则调用LockSupport.unpark()方法后,该线程会被唤醒。
而如果thread之前没有调用LockSupport.park()方法,则调用LockSupport.unpark()方法后,再调用LockSupport.park()方法,其会立刻返回。
public class LockSupportMain {
public static void main(String[] args) {
System.out.println("main线程----------park前");
//使用当前线程获取到许可证
LockSupport.unpark(Thread.currentThread());
LockSupport.park();
System.out.println("main线程----------park后");
}
}
*******************************************
main线程----------park前
main线程----------park后
public class LockSupportMain {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("子线程开始...");
//调用LockSupport.park()方法,挂起自己
//默认情况下,子线程没有持有许可证,因而它会把自己挂起。
LockSupport.park();
System.out.println("调用park后,改行不会打印,直到LockSupport.unpark(thread)唤醒后,
才会打印这行,子线程才会结束.");
});
thread.start();
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//10s后,main线程调用LockSupport.unpark()方法让thread线程持有许可证,
//然后LockSupport.park()方法返回。
LockSupport.unpark(thread);
}
}
LockSupport的使用过程中还需要注意以下几点:
- 和wait方法一样,park方法也需要在循环体中使用。这是为了防止线程被唤醒后,不进行判断而意外继续向下执行。
- park方法是会响应中断的,但是不会抛出异常。(也就是说如果当前调用线程被中断,则会立即返回但不会抛出中断异常)
- park的重载方法park(Object blocker),会传入一个blocker对象,所谓Blocker对象,其实就是当前线程调用时所在调用对象(如上述示例中的FIFOMutex对象)。该对象一般供监视、诊断工具确定线程受阻塞的原因时使用。