【问题标题】:Best practices for handling InterruptedException [duplicate]处理 InterruptedException 的最佳实践 [重复]
【发布时间】:2019-03-08 10:03:53
【问题描述】:

在业务场景中,InterruptException 发生多次,有的在业务代码执行之前有的在业务代码执行之后。如何处理InterruptException让我很困惑。

1. preBusiness 代码 semaphore.acquire()

        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
           // do something
        }
        resObj = caller.stepTry();
        semaphore.release();
  1. 发布企业代码 latch.await(), service.take().get()

    CompletionService<CallableResultBO> service = new ExecutorCompletionService<>(executor);
    CountDownLatch latch = new CountDownLatch(size);
    
    for (R callable : listCall){
        callable.setCountParam(JdkThreadCountBO.buildByLatch(latch));
        service.submit(callable);
    }
    
    try {
        latch.await();
    } catch (InterruptedException e) {
        // do something
    }
    
    CallableResultBO[] resArr = new CallableResultBO[size];
    for ( int i = 0; i < size; i++ ){
        try {
            resArr[i] = service.take().get();
        } catch (InterruptedException e) {
            // do something
        } catch (ExecutionException e) {
            // do something
        }
    }
    

    在实践中也发现了一些疑惑,我还在思考如何下结论。 一个线程不能随便打断。即使我们为线程设置了中断状态,它仍然可以获得CPU时间片。通常只有被 sleep() 方法阻塞的线程才能立即收到 InterruptedException,所以在睡眠中断任务的情况下,可以使用 try-catch 跳出任务。其他情况需要通过判断线程状态来判断是否需要跳出任务(Thread.interrupted()方法)。

         另外,同步方法修改的代码在收到中断信号后不会立即中断。 ReentrantLock锁控件的同步代码可以被InterruptException打断。

【问题讨论】:

  • 如果我有时间,我稍后会写一个全面的答案,但这个问题的最终答案是阅读 JCiP,Goetz 等人的 Java Concurrency in Practice 一书al,它详细地解决了这个问题。
  • 感谢您的回复。我现在负责一个订单履行中心,经常写并发代码,经常遇到这个异常。这本书和讨论我都看过,但我的一些疑问还没有解决。虽然 Thread.sleep() 可以抛出这个异常,但它并不代表测试这个异常。与“深度java原理”相比,我需要从业务的角度来探索这个异常的最佳实践。
  • 我不确定我是否理解您的要求,或者“深度 Java 原则”和“最佳实践”之间的区别。如果您不理解异常意味着,那么您编写的代码将不是最佳实践。 InterruptedException 的意思是“我正在做一些涉及等待的事情,但是有人告诉我要快点停止等待,所以我没有完成涉及等待的事情”。处理InterruptedException 的正确方法完全取决于涉及等待的情况以及谁可能中断了它。

标签: java exception concurrency


【解决方案1】:

一般情况下,建议您执行以下操作:

    void methodThatWaits()
    {
        try 
        {
            Thread.sleep( 1000 );
        }
        catch( InterruptedException e )
        {
            //e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }

所以,不,Thread.currentThread().interrupt(); 不是多余的。

这被称为 Java Thread.currentThread().interrupt 习惯用法,在Java 并发实践,第 7.1.3 章中有详细说明。 Effective Java中也提到过,你可以在这里阅读摘录:Google Books - Effective Java - search for java idiom thread interrupt interruptedexception josh bloch

【讨论】:

    【解决方案2】:

    所有 java.util.concurrent 类统一使用中断,表示接收线程应该结束它正在做的事情并终止。在我看来,在编写使用这些类的代码时遵循相同的准则才有意义。

    当某些东西抛出 InterruptedException 时,该线程的中断标志被清除。调用 Thread.currentThread().interrupt 将标志恢复为之前的值。

    对于教程中的玩具示例,恢复标志似乎很愚蠢,因为它位于 run 方法末尾的 catch 块中,并且线程无论如何都会立即终止。关键是一个线程中可能有很多层的事情,你可能有一个可运行的任务提交到一个线程池,该任务正在与阻塞队列对话,等等,每个参与者都需要知道是否发生中断所以他们都可以到达一个停止点并优雅地完成。如果其中任何一个在没有恢复标志的情况下吃掉了 InterruptedException,那么有人可能会错过有关中断的消息,线程将继续工作,并且您的应用程序可能无法完全退出。

    没有干净地完成可能意味着:

    • 因此,可能会有一半完成的工作挂在外面晾干。

    • 线程是 GC 根,因此您可能会遇到内存泄漏。

    • 任何保持活动状态的非守护线程都会阻止 JVM 退出。

    关于如何处理 InterruptedException:java.util.concurrent 中的同步器倾向于让它被抛出。你是否这样做取决于你需要做什么来完成事情。线程取消是自愿的,因此您可以确保可靠地清理您的任务。

    【讨论】:

      猜你喜欢
      • 2012-10-18
      • 1970-01-01
      • 1970-01-01
      • 2013-08-25
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多