【问题标题】:Calling interruptible blocking methods in a loop在循环中调用可中断的阻塞方法
【发布时间】:2013-07-07 03:24:30
【问题描述】:

这个问题来自Brian Goetz的“Java并发实践”一书中的一个例子,第7章,7.1.3响应中断(第143-144页) 书上说的

不支持取消但仍调用可中断阻塞方法的活动将不得不循环调用它们,并在检测到中断时重试。在这种情况下,他们应该在本地保存中断状态并在返回之前将其恢复,如下例所示,而不是在捕获 InterruptedException 时立即恢复。过早设置中断状态会导致无限循环,因为大多数可中断阻塞方法在进入时检查中断状态,如果设置了立即抛出InterruptedException......

    public Task getNextTask(BlockingQueue<Task> queue) {
    boolean interrupted = false;
    try {
      while (true) {
       try {
         return queue.take();
       } catch (InterruptedException e) {
           interrrupted = true;
       }
      }
    } finally {
      if (interrupted) 
          Thread.currentThread().interrupt();
      }
}

我的问题是为什么需要循环?

另外,如果 queue.take() 抛出了一个 interruptedException,那么我假设在当前线程上设置了中断标志对吗?那么下一次调用 queue.take() 将再次抛出 interruptedException,因为当前线程上的上一个中断没有被清除,这不会导致无限循环吗?

【问题讨论】:

    标签: java multithreading concurrency


    【解决方案1】:

    回答你的第一个问题

    My question is why is the loop required?
    

    在这一行

           Activities that do not support cancellation but still call interruptible 
    blocking methods will have to call them in a loop, retrying when interruption 
    is detected.  
         From Java concurrency in Practice 7.1.3
    

    getNextTask 方法不支持取消。即即使线程被中断,它也不会取消它的任务并会重试。请注意,getNextTask 方法正在调用一个可中断的阻塞方法 queue.take(),它会抛出 intteruptedException。 getNextTask 方法将不得不处理 interruptedException,因为它不支持取消并且应该重试。 简单来说,它是一种方法策略,它决定重试还是在方法签名中抛出被中断的异常。

    你的第二个问题

    Also if queue.take() throws an interruptedException then I am assuming the 
    interrupt flag is set on the current thread correct? 
    

    否,如果抛出中断异常,则重置中断标志。

    在finally块中还要注意,线程是自中断的(Thread.currentThread().interrupt()),因为getNextTask的调用栈也可能调用其他可中断的阻塞方法,这样的方法一般会先检查当前是否线程是否被中断。如果是,则中断标志被重置并出现中断异常 像下面这样抛出。

    Below is the Code from AQS(AbstractQueueSynchronizer) from java
    
         public final void acquireInterruptibly(int arg) throws InterruptedException {
                if (Thread.interrupted())
                    throw new InterruptedException();
                if (!tryAcquire(arg))
                    doAcquireInterruptibly(arg);
            }
    

    【讨论】:

      【解决方案2】:

      因为您已经决定 getNextTask 不会抛出异常。当queue.take() 不起作用时,唯一要做的就是绕过并重试。返回 null 相当于抛出异常,调用代码可能没有为此做好准备。摆脱此方法的唯一方法是使用良好的值或 RunTimeException。 (对我来说似乎有点极端,但毫无疑问这是有道理的。)

      您实际上并没有查看中断标志;它的条件不会影响此代码。您正在仔细地设置它,以便调用程序在它确实得到它的Task(或 RunTimeException)时可以知道有什么东西试图中断它。 (不要把局部变量interrupted和Thread方法interrupted()混淆了!)

      【讨论】:

        【解决方案3】:

        否则,您将无法保证按照您的方法签名的要求返回Task。没有循环,考虑方法:

        public Task getNextTask(BlockingQueue<Task> queue) {
          boolean interrupted = false;
          try {
            return queue.take();
          } catch (InterruptedException e) {
            interrupted = true;//No return here, the compiler will complain
          } finally {
            if(interrupted) {
              Thread.currentThread().interrupt();
            }
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2014-10-26
          • 1970-01-01
          • 1970-01-01
          • 2018-06-11
          • 2022-08-20
          • 2020-01-25
          • 2018-06-09
          • 2017-11-08
          • 1970-01-01
          相关资源
          最近更新 更多