代码之家  ›  专栏  ›  技术社区  ›  Harry Geoffrey Trebing

Java中“中断异常”的正确处理

  •  0
  • Harry Geoffrey Trebing  · 技术社区  · 2 年前

    因此,我遇到了一个问题,Sonar Qube由于处理不当而标记了我的代码 InterruptedExceptions .Sonar Qube的准确误差为 java:S2142 ,可以在此处找到: https://github.com/joansmith/sonar-java/blob/master/java-checks/src/main/resources/org/sonar/l10n/java/rules/squid/S2142.html

    我的代码如下-Sonar Qube正在标记可能的处理 中断的异常 在里面 extractFutureIntoList() 作为问题的根源:

    @Service
    @Log4j2
    public class AsynchronousDataGrabber {
    
      ExecutorService executorService = Executors.newFixedThreadPool(10);
    
      public List<MyDataObject> getDataAsynchronously() {
        Optional<Future<MyDataObject>> future01 = getDataFuture("01");
        Optional<Future<MyDataObject>> future02 = getDataFuture("02");
        Optional<Future<MyDataObject>> future03 = getDataFuture("03");
    
        List<MyDataObject> list = new ArrayList();
        
        extractFutureIntoList(future01, list);
        extractFutureIntoList(future02, list);
        extractFutureIntoList(future03, list);
        
        return list;
      }
    
    
      private Optional<Future<MyDataObject>> getDataFuture(String key) {
        try {
          return Optional.of(executorService.submit(() -> getDataFromRemoteApi(key)));
        } catch(Exception e) {
          log.error("Exception", e);
          return Optional.empty();
        }
      }
      
      private void extractFutureIntoList(Optional<Future<MyDataObject>> future, List<MyDataObject> list) {
        try {
          if (future.isPresent()) {  
            // This is apparently where the `InterruptedException` can occur - Future.get()
        list.add(future.get().get());  
          }
        } catch (Exception e) {
          // SonarQube is telling me that merely logging an `InterupptedException` is not enough
          // Apparently, I must either rethrow the `InterruptedException`,
          //or call `Thread.currentThread().interrupt()
          log.error("Exception", e);
          return;   
        }
      }
    }
    

    Sonar Qube建议我通过重新思考来解决这个问题 InterruptedException ,或拨打 Thread.currentThread().interrupt() --每当 Future.get() 被中断。

    我的问题是 getDataAsynchronously() 正在由我的应用程序的主线程调用。如果对的唯一可接受的响应 中断的异常 是重新抛出它或中断当前线程,然后在我的一个线程中进行一次中断 executorService's 线程将关闭我的整个应用程序。这似乎是一个过度的响应,特别是考虑到线程正在运行的任务- getDataFromRemoteApi() -无论如何,可能并不总是成功的。

    有更好的处理方法吗 中断的异常 -理想情况下是Sonar Qube可以接受的,但不涉及杀死试图调用的线程 Future.get() ?

    我已尝试记录 中断的异常 ( log.error("Exception", e); ),我试图抓住并重新思考 中断的异常 ( throw new RuntimeException(e); )-两人都没有安抚索纳尔·库贝。

    2 回复  |  直到 2 年前
        1
  •  1
  •   gdomo    2 年前

    你永远不应该忽视 InterruptedException 的确

    Risen 中断的异常 意味着正在运行的线程被标记为中断,该信号(可以用 Thread.currentThread.isInterrupted() )收到,并且 已删除 。所以在不吐/不叫的情况下抓住它 Thread.currentThread().interrupt() 导致永远忘记中断的事实。

    如果你抓到了 中断的异常 从方法执行优雅的返回并抛出异常,或者恢复标志并返回。

    在您的特定情况下,获得 中断的异常 在指定行中表示您的 主要的 线程已被中断(不是执行程序的)。这意味着有人停止了程序,因此不再对方法返回值感兴趣。我建议你:

    1. 呼叫 Thread.currentThread().interrupt() 然后 throw new RuntimeException(e) 。没关系——你们都保持着中断的标志,并表示你们 getDataAsynchronously() 没有正确执行,并且没有返回的答案。
    2. 尽“最大努力”-呼叫 Thread.currentThread().interrupt() 然后从 extractFutureIntoList 。您的程序将继续执行 extractFutureIntoList 还剩个电话。如果任何其他期货都已经完成,因此它们的结果可以立即获得, future.get() 将返回计算结果而不抛出 中断的异常 。因此,您将从线程中断时完成的所有期货中收集数据。这将是一种优雅的关闭。
    3. 如果可能,标记 getDataAsynchronously throws InterruptedException 并重新抛出异常。这是一个最佳实践-将任何阻塞方法标记为 throws InterruptedException .你的方法 异步获取数据 是拦网,实际上意味着要投球。
        2
  •  1
  •   Laird Nelson    2 年前

    捕获 InterruptedException 还清除线程的中断状态。如果你决定吃 中断的异常 ,那么在任何情况下我都能想到你应该打电话 Thread.currentThread().interrupt() 将线程重新标记为已中断,以便对该线程的任何其他调用 Thread.isInterrupted() 将报告发生中断。另一个可行的替代方案(SonarQube建议)是更改各种方法签名,以声明 中断的异常 可以抛出,然后不要捕获异常。这两种方法都确保了线程在某个时刻被中断的事实对相关方是可用的。

    在几乎所有情况下,我都能想到不能调整方法签名的地方,第一个选项正是你想要做的。

    这听起来像是出于任何(可能是有效的)原因,也许你不想这样做,所以假设你的情况是有效的,我想你可以(正如SonarQube所建议的)简单地记录异常,而不重置线程的原始中断状态,但要注意的是,在这种情况下,你正在丢失信息(一切都会看起来像从未发生过中断一样)。