跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • AQS 里的 doAcquireInterruptibly,如果已经阻塞在 park 了,是不可能被唤醒的吧?
未分類
19 5 月 2020

AQS 里的 doAcquireInterruptibly,如果已经阻塞在 park 了,是不可能被唤醒的吧?

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)

  • 資深大佬 : hfc

    .interrupt()也能使线程从.park()引起的线程等待状态中恢复

  • 主 資深大佬 : amiwrong123

    @hfc
    是嘛,在 interrupt 的源码里能看到有调用 unpark 的吗

  • 資深大佬 : hfc

    @amiwrong123 不行,是 native 本地方法了,但是从其注释中应该可以略知一二

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具