【问题标题】:When to call Thread.currentThread().interrupt() and when not to call?什么时候调用 Thread.currentThread().interrupt() 什么时候不调用?
【发布时间】:2019-12-25 18:40:39
【问题描述】:

来自互联网上的多篇文章,建议不要吞下InterruptedException。当我要重用同一个线程时,使用类似这样的线程池执行器执行此操作更有意义。

public static void main(String[] args) throws InterruptedException {

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<?> future = executor.submit(() -> {
        printNumbers(); // first call
        printNumbers(); // second call
    });
    Thread.sleep(3_000);                                     
    executor.shutdownNow();  // will interrupt the task
    executor.awaitTermination(3, TimeUnit.SECONDS);
}

private static void printNumbers() {
    for (int i = 0; i < 10; i++) {
        System.out.print(i);
        try {
            Thread.sleep(1_000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // preserve interruption status
            break;
        }
    }
}

上面来自DZone的代码示例。

但在每次都创建新线程的情况下:

Object LOCK = new Object();

public void doSomeJob() {

    myThread = new Thread(new Runnable() {
        public void run() {
            try {
               synchronized(LOCK) {
               System.out.println("Inside run");
               LOCK.wait();
              }
            } catch(InterruptedException ignored){}
        }
    }
}

我还需要打电话给Thread.currentThread().interrupt();吗?这有意义吗?

好的参考:

https://codepumpkin.com/interrupt-interrupted-isinterrupted-java-multithreading/

http://michaelscharf.blogspot.com/2006/09/dont-swallow-interruptedexception-call.html

【问题讨论】:

  • 旁白:你也可以把 try/catch InterruptedException 放在循环之外,因为中断具有打破循环的效果。
  • 好收获。你是对的!
  • 我根据 DZone 更正了循环,据我所知,打破循环是 sample 的作者的意思。如果不同意,请添加注释说明代码已从原始代码更改。
  • mmmm 我看到了一个等待方法,但我没有看到任何同步块。你需要获得一个对象的锁才能使用等待方法释放它...我的意思是:您应该放置一个 synchronized (lock) {...} 块来包装等待调用
  • 只是另一个解释一些相关问题的链接:javaspecialists.eu/archive/Issue056.html

标签: java multithreading interrupt


【解决方案1】:

我将根据 Brian Goetz 的好书 Concurrency in Practice7.1.2 部分给出答案。

在您的第一个示例中,您使用ExecutorServiceExecutorService 管理它自己的线程。您不是这些 Threads 的所有者,因此您不知道中断对它们意味着什么(例如,ThreadPool 可能会选择杀死 Threads中断并创建新的)。这就是为什么在向此池提交可取消任务时应保留中断状态的原因。此引文适用于本案:

任务不在它们拥有的线程中执行。它们借用服务拥有的线程,例如线程池。代码 不拥有线程(对于线程池,线程池实现之外的任何代码)应该小心 保留中断状态,以便拥有代码最终可以对其进行操作,即使“访客”代码对 中断也是如此。 (如果您正在为某人看家,您不会在他们不在时将收到的邮件扔掉 - 您将其保存并让他们在他们回来时处理它,即使您确实阅读了他们的杂志。)

在第二种情况下,您手动管理 Thread 的实例。所以你是它的所有者。因此,您决定中断对这个 线程 意味着什么,如果您不想应用任何 线程中断,则不必在第二种情况下保留 中断状态政策

你不应该做的是通过捕获 InterruptedException 并在 catch 块中什么都不做来吞下它,除非你的代码实际上正在为线程实现中断策略

另请注意,线程中断政策不同于任务取消政策

  1. 线程中断策略 - 定义线程如何响应中断(例如ThreadPool 可能会杀死被中断的线程并创建一个新线程)。它由线程的所有者定义。
  2. 任务取消政策 - 定义任务对取消的反应。取消通常是通过中断来实现的。执行任务的人选择任务是否响应中断。如果您的任务调用抛出InterruptedException 的方法,这很容易实现。或者您可以通过调用Thread::isInterrupted(例如在循环中)来检查线程的中断标志。任务的实现者选择如何处理。

此外,您不应该对线程中断策略进行任何假设(如果您不是Thread 的所有者)。这就是为什么保留中断状态或重新抛出InterruptedException 被认为是一种好的做法的原因。

【讨论】:

  • 这是正确答案!最好将其标记为正确答案
  • 好收获。输入问题时忘记添加synchronized(LOCK)。已更新。
  • 那么,什么时候可以决定不为线程关联任何Thread Interruption Policy
  • @gooogle 线程的所有者决定如果线程正在执行的任务被中断该怎么办。您可以选择什么都不做,但也许所有者想做一些事情——清理一些资源,杀死线程并重新创建它等等。这完全取决于线程的所有者。
【解决方案2】:

如果您的锁来自java.util.concurrent.locks.Lock 并且是可中断的(使用.lockInterruptibly()),那么中断进程是有意义的,这样一切都可能被中断和取消。
documentation 阅读章节Implementation Considerations

但如果您的锁是non-interruptible(使用.lock()),那将没有意义,因为您将无法中断锁。

在您的情况下,您使用的是wait(),它可以像here那样被中断,并且会抛出InterruptedException

【讨论】:

  • 那么在wait()的情况下,我还需要打电话给Thread.currentThread().interrupt()吗?
  • 你可以中断任何线程,它会导致锁释放。确定要中断的线程将取决于您的代码实现。如果你想中断整个进程,你应该中断你的主线程。
  • 有点害羞地问这个问题,但是,中断这个线程会如何中断我的主线程?
  • 你也可以用myThread.interrupt()中断你的一个子线程
  • @gooogle 它吞下了所讨论的中断here :)
【解决方案3】:

您问题中DZone链接https://dzone.com/articles/understanding-thread-interruption-in-java中的解释非常详细。 Thread.currentThread().interrupt(); 引发中断的异常状态,该状态之前通过阻塞方法 (sleep) 清除。这样做是为了确保第二个循环也被中断(它会在同一个线程上捕获异常)。

在我结束之前,我想强调一个重要的细节 阻塞代码时线程的中断状态会发生什么 通过抛出 InterruptedException 来响应中断。我离开了 直到现在为止的细节,以避免混淆。

在阻塞代码抛出 InterruptedException 之前,它会标记 中断状态为假。因此,当处理 InterruptedException 完成后,您还应该保留 中断状态通过调用Thread.currentThread().interrupt()。

让我们看看这些信息如何应用于下面的示例。在里面 提交给 ExecutorService 的任务,printNumbers() 方法被调用两次。当任务被调用中断时 toshutdownNow(),对方法的第一次调用提前结束,然后 执行到达第二个调用。中断由 主线程只有一次。中断被传达给 通过调用第二次执行 printNumber() 方法 第一次执行期间的 Thread.currentThread().interrupt()。因此 第二次执行也会在打印第一次之后提前完成 数字。不保留中断状态会导致 第二次执行该方法完全运行 9 秒。

在哪里使用Thread.currentThread().interrupt(); 取决于您的代码,第二个示例并不完整,无法理解对它的需求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-27
    • 2010-10-18
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多