在阅读AQS 源码的时候有一个地方让我十分在意,就是线程被Lock 阻塞后唤醒时要对interrupt 进行状态检查,调用Thread.interrpted() 获取当前线程的中断状态并且将状态复位, 并且保存这个状态后返回到堆栈上层。

上层对这个状态判断,如果是中断状态就去调用Thread.currentThread.interrupt() 中断。

一开始看到的时候不知道有什么作用, 为什么要特意去通过interrupted返回当前线程中断状态后去复位这个状态,然后由堆栈上层判断返回的结果再去补偿复位,后面看多几遍代码后才知道他的作用。

首先呢,如果线程被中断, 那么线程的阻塞状态就会结束,如果中断状态没有复位,那么多有可能导致线程阻塞的方法都会失效,包括Thread.currentThread().sleep(), LockSupport.park(), 这些方法调用后立刻会被检测到线程中断并终止操作。

ASQ 线程竞争重试步骤:

1、 以轻量级锁也就是自选的方式竞争锁

2、 如果竞争失败,则放入等待队列中, 并通过LockSupport.pork() 阻塞线程。

3、 线程被唤醒后会继续以自旋的方式竞争锁,重复1、2步骤。

** 线程被唤醒可能是其他线程锁释放后唤醒,也可能是线程被中断后唤醒, 如果线程被中断后不复位中断标识,那么在步骤1自旋竞争锁失败的时候调用LockSupport.pork() 就会直接失效,最终的结果就是无法阻塞线程,导致线程竞争锁时无限自旋,浪费cpu 资源

因此JDK 的解决方案就是在自旋过程中每次唤醒后都清除中断标记并且记录到变量中,在自旋竞争锁成功之后返回到堆栈上层对中断标识进行补偿。

发表评论

电子邮件地址不会被公开。 必填项已用*标注