AQS 里的 doAcquireInterruptibly,如果已经阻塞在 park 了,是不可能被唤醒的吧?
資深大佬 : amiwrong123 0
AQS 里的 doAcquireInterruptibly 时,如果已经阻塞在 park 了,且当前持有锁的线程就是不释放锁,那阻塞在 park 的线程是不可能被唤醒的吧?
private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return; } if (shouldParkAfterFailedAcquire(p, node) &&//设置好上一个 node 的 signal 信号 parkAndCheckInterrupt())//然后就阻塞 throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } private final boolean parkAndCheckInterrupt() { LockSupport.park(this);//直接阻塞的,只有唤醒后才知道当前线程有没有中断过 return Thread.interrupted(); }
从上面代码的注释处,可以看出,当同步器的锁已经被别的线程获得,且当前线程没有中断过时,当前线程执行 doAcquireInterruptibly 的流程是:addWaiter 加入当前线程的 node,然后 shouldParkAfterFailedAcquire 设置一下上一个 node 的 signal 信号,然后就 parkAndCheckInterrupt 阻塞了,也就是说,刚开始这 for 循环只执行一次 就阻塞了。
根据上面的论据,使用下面例子:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class test3 { public static void main(String[] args) throws InterruptedException { final Lock lock=new ReentrantLock(); lock.lock(); Thread t1=new Thread(new Runnable(){ @Override public void run() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName()+" interrupted."); } System.out.println(Thread.currentThread().getName()+"结束了"); } }); t1.start(); Thread.currentThread().sleep(5000); t1.interrupt(); while (true){} } } 打印结果: java.lang.InterruptedException at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) at test3$1.run(test3.java:19) at java.lang.Thread.run(Thread.java:745) Thread-0 interrupted. Thread-0 结束了
- 首先主线程获得了锁,且最后执行死循环,所以永远不释放锁。
- 在主线程睡觉期间,子线程已经阻塞在 parkAndCheckInterrupt 里了。
- 等主线程睡醒了,设置子线程的中断状态。但主线程永远不释放锁,那永远不会有人调用
unpark(子线程),那子线程岂不是应该永远阻塞吗?
所以为什么上面这个例子,最后还是抛出了异常?因为都没有人调用unpark(子线程)。
大佬有話說 (3)