LockSupport源码解析
LockSupport是JDK在rt包里面提供的一个工具类,它的主要作用是挂起和唤醒线程。LockSupport是创建锁和其他同步类的基础。
LockSupport类与每个使用它的线程都会关联一个许可(permit),permit的值只有0或1,默认是0.
- 当前用unpark(thread)方法时,会将指定线程的许可permit设置为1。unpark方法是不可重入的,多次调用之后,许可permit的值还是为1.
- 当调用park方法时,如果当前线程的许可permit是1,那么将许可permit设置为0,并立即返回。如果当前线程的许可permit是0,那么当前线程就会阻塞,直到别的线程将当前线程的许可permit设置为1.park方法会将许可permit再次设置为0,并返回。
- 在未调用过unpark方法之前调用park方法,因为许可permit为0,调用线程会被阻塞,调用unpark方法之后,线程才会被唤醒。
- 线程如果被中断了,也会从park的阻塞方法处被唤醒
- 还有一种线程从park方法处返回的可能是,不知道为什么,线程自己就返回了。
LockSupport使用示例
首先,尝试如下代码:
1 | public class LockSupportTest { |
在调用unPark之前,调用park方法,可以看到,当前只输出了Before park
之后就不再输出了,这说明,当前线程确实已经被阻塞住了。
接着我们把上面的例子再改一下,在调用park之前,调用unpark先:
1 | public class LockSupportTest { |
发现,能正常打印出两个输出语句。
接着再看一个例子:
1 | public static void main(String[] args) throws InterruptedException { |
上面的例子中,新起了一个线程thread,然后在它的run方法中调用了LockSupport的park方法,因为之前thread并没有调用过unpark方法,因此,线程thread会阻塞在park方法这。接着,主线程在睡眠1秒之后,开始调用unpark方法,这个时候,线程thread就会从阻塞处恢复,接着打印出Child thread after park
。
运行一下,看下效果:
最后再看看,线程被中断之后,从park出返回的例子,代码修改如下:
1 | public static void main(String[] args) throws InterruptedException { |
代码逻辑很简单,就不再赘述,直接看运行结果:
最后,我们再看一个JDK提供的LockSupport方法的示例:
1 | import java.util.Queue; |
LockSupport常用方法
park
如果调用park方法的线程已经拿到了与LockSupport关联的许可permit,则调用park方法时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
unpark(Thread thread)
当一个线程调用unpark时,如果参数thread线程没有持有thread与LockSupport类关联的许可permit,则让thread持有。如果thread之前因调用park而被阻塞挂起,则调用unpark后,线程会被唤醒。如果thread之前没有调用park,则调用unpark后,在调用park方法,其会立刻返回。
parkNanos(long nanos)
如果调用park的方法已经拿到了与LockSupport关联的许可permit,则调用LockSupport.parkNanos方法后会马上返回。该方法的不同在于,如果没有拿到许可permit,则调用线程会被挂起nanos时间后修改为自动返回。
park(Object blocker)
当线程在没有持有许可permit的情况下调用park方法而被阻塞挂起时,这个block对象会被记录到该线程的内部。
使用诊断工具可以观察线程被阻塞的原因,诊断工具是通过调用getBlocker(Thread)方法来获取blocker对象的,所以JDK推荐我们使用带有blocker参数的park方法,并且blocker设置为this,这样当在打印线程堆栈排查问题时就能知道是哪个类被阻塞了。