LockSupport工具类主要用来挂起和唤醒线程。它会与每个使用它的线程关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。底层实现也是使用的Unsafe类,而且使用的是本地方法。介绍一下几个函数:
1.park()和unpark()方法
void park()方法
若调用park方法的线程已经拿到了与LockSupprot关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程调度,即被阻塞挂起。如下代码:
public static void main(String[] args) throws Exception {
System.out.println("begin park!");
LockSupport.park();
System.out.println("end park!");
}
结果只会输出"begin park!",因为默认情况下调用线程不持有许可证。
在其他线程如OtherThread调用LockSupport.unpark(Thread thread)方法时,若thread因为调用park()方法被阻塞,则此时会返回;若其他线程调用了阻塞线程的interrupt()方法,即在OtherThread调用了thread.interrupt()方法,设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回,即阻塞的线程是响应中断的。
void unpark(Thread thread)方法
当一个线程OtherThread调用LockSupport.unpark(thread)时,若参数thread没有持有thread与LockSupport的许可证,则会让thread线程持有;若thread之前因调用park()而被挂起,则调用unpark(thread)后,该线程会被唤醒;若thread之前没调用过thread.park()方法,则调用unpark(thread)方法后,再调用thread.park()方法,会立即返回。看一个例子:
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("child thread begin park!");
LockSupport.park();
System.out.println("child thread unpark!");
}
});
thread.start();
Thread.sleep(1000);
System.out.println("main thread begin unpark!");
LockSupport.unpark(thread);
}
运行结果如下。首先thread使用park方法将自己挂起,然后主线程输出语句后调用LockSupport.unpark(thread)方法让子线程持有许可证,子线程调用的park()方法就返回了。
child thread begin park!
main thread begin unpark!
child thread unpark!
总结
其实LockSupport方法中的park()和unpark()方法用来阻塞或恢复线程,其实就是通过一个抽象的概念即:当前线程是否有许可证。
- 若线程第一次调用park()方法,默认没有许可证,所以会被挂起。等待其他线程调用unpark(thread)将被挂起的线程做为参数时,才能返回。
- 若线程没有调用过park()方法,则调用了unpark(thread)方法后,已经把许可证给了thread了,所以此后若thread再调用park()方法,会立即返回。
- 要注意的是,因调用park()方法而被阻塞的线程被其他线程中断而返回时并不会抛出InterruptedException异常,而且会立即返回。
2.park()方法的变形
void parkNanos(long nanos)方法
和park方法类似,只是如果线程在nanos时间内还未拿到许可证,则会自动返回,而park()则必须等到unpark()的执行才能返回。
park(Object blocker)方法
若线程在没有许可证的情况下调用park()方法而被阻塞挂起,这个blocker对象会被记录到该线程内部。这个方法还是挺有用的,一些诊断工具可以观察线程被阻塞的原因,而诊断工具往往就是通过调用getBlocker(Thread)方法来获取blocker对象从而判断是在哪一个类中被阻塞了。通过使用park(this)方法将当前对象作为参数传进去,就可以获得那个类。代码如下:
public static void park(Object blocker) {
Thread t = Thread.currentThread();
//设置该线程的blocker变量
setBlocker(t, blocker);
//挂起线程,返回时就会从这个方法返回,进而在下一步清除blocker变量
UNSAFE.park(false, 0L);
//线程被激活后清除blocker变量,因为一般都是在线程阻塞时才分析原因
setBlocker(t, null);
}
Thread类中有个变量volatile Object parkBlocker,用来存放park方法传递的blocker对象,也就是把blocker变量存放到了调用park方法的线程的成员变量里面。
void parkNanos(Object blocker, long nanos)方法
相对于park(Object blocker)方法多了个超时时间
void parkUtil(Object blocker, long deadline)方法
它的代码如下:
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
此方法和parkNanos(Object blocker, long nanos)的区别是参数为nanos表示从当前算等待nanos秒时间后返回,而参数为deadline表示的是指定的一个时间点,该时间是从1970年到现在某一个时间点的毫秒值。