interrupt()中断 && LockSupport.park()/unpark()

中断状态

Thread 对象 native 实现中有一个标识位属性代表线程的中断状态,可以认为它是一个 Boolean 类型的变量,初始值为 false

通过调用一个线程的isInterrupted()方法可以判断该线程是否处于中断状态

通过调用一个线程的interrupt()方法将该线程中断,该操作只是将该线程的中断标志置为 true,至于线程以何种动作处理该中断就要看线程自己

等待状态

当我们调用Object.wait(), Object.join(), LockSupport.park()方法时,会使运行状态的线程变为等待状态

当我们调用Object.notify(), Object.notifyAll(), LockSupport.unpark()方法时,会使等待状态的线程变为运行状态

park()/unpark()

前面说过,中断可以唤醒处于等待状态的线程,而调用LockSupport.park()正好可以使线程处于等待状态,那么是否可以使用interrupt()LockSupport.park()实现无限次的唤醒和等待呢?

现在就可以看出很明显的问题,我们只中断了一次,为什么会输出 10 个呢??不应该只输出 1 个就继续等待了吗?

我们先来仔细介绍一下park()/unpark()通信机制,它们俩之间是通过「许可 (permit)」来通信,是一个整型变量,它的数量是不允许叠加的,只有 0 和 1 两种状态,0 表示没有许可,1 表示有许可

调用unpark()会将许可 +1,如果已经是 1 就不变;调用 park() 会将许可 -1,如果已经是 0 就不变,但是会等待新的许可来才能被唤醒

它们俩有一个独特的性质:不需要管调用的先后顺序,可以先调用unpark()获得一个许可,后面再调用park()使用掉这个许可

注意:如果先调用unpart(),必须保证线程已经启动,也就是执行了start()方法,否则没有效果。详情可见 JDK 注释:

park()是调用UNSAFE.park(false, 0L)方法,而UNSAFE.park(false, 0L)是一个本地方法,在 os_posix.cpp 中。代码太长,看不懂???直接上简化版的伪代码:

当 permit = 0 时表示线程处于等待状态,当 permit = 1 时表示线程处于唤醒状态,所以park()/unpark()就是 permit 处于 0 和 1 之间的变化

可以看出只要「permit = 1」或者「中断状态 = true」,那么执行park()就不能够阻塞线程,这也解释了为什么上面只调用了一次中断,后续就不会因park()而等待

同理unpark()简化版的伪代码如下:

interrupt() && unpark()

简化版的伪代码如下:

interrupt()会设置中断状态为 true,同时调用一次unpark(),将 permit 设置为 1

interrupt() && sleep()

简化版的伪代码如下:

sleep()会去检测中断状态,如果中断状态为 true,就消耗掉它 (置为 false) 并抛出异常

注意:wait(), join()sleep()

参考文章