在 try 块完成后延迟 Finally 块

Delay in the Finally block after try block completion

我正在尝试 运行 下面的并发代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThread {

public static void main(final String[] arguments) throws 
InterruptedException {
  ExecutorService executor = Executors.newSingleThreadExecutor();

  try {
     executor.submit(new Task());
     System.out.println("Shutdown executor");
     executor.shutdown();
     executor.awaitTermination(5, TimeUnit.SECONDS);
  } catch (InterruptedException e) {
     System.err.println("tasks interrupted");
  } finally {

     if (!executor.isTerminated()) {
        System.err.println("cancel non-finished tasks");
     }
     executor.shutdownNow();
     System.out.println("shutdown finished");
  }
 }

static class Task implements Runnable {

  public void run() {

     try {
        int duration = 6;
        System.out.println("Running Task!");
        TimeUnit.SECONDS.sleep(duration);
     } catch (InterruptedException e) {
        e.printStackTrace();
     }
  }
  }     
}

输出为:

Shutdown executor
Running Task!
shutdown finished
cancel non-finished tasks
java.lang.InterruptedException: sleep interrupted

根据输出,finally 块似乎被跳过,finally 块之后的代码先执行,然后再执行 finally 块。这是否与 try/catch 块完成后最终执行的代码的正常流程相矛盾?

编辑:已尝试

System.out.flush();
System.err.flush();

每次按照其中一项建议打印后,输出仍然相同。

编辑:

我正在使用在线编译器。

我想您希望这两个跟踪被颠倒,因为它们是以相反的顺序声明的:

shutdown finished

cancel non-finished tasks

我认为问题来自 System.errSystem.out 的混合。
这些不是相同的流。所以它们的 flushing 和它们的 displaying 可能在不同的时间执行。

根据显示输出的 application/system(IDE、OS 命令行、在线 compiler/executor),至少有两件事可能会导致排序问题:

  • 这些流的 autoflush 可能启用或未启用
  • application/system 的 "output/console" 中这两个流之间的跟踪显示顺序可能不同步。

作为根据时间线显示输出的解决方法:

  • 在每次 print() 调用后刷新流(System.out.flush()System.err.flush())。
    它可能有效,但不能保证 application/system 写入输出可能不会通过时间线同步这两个流的显示。

  • 尽量仅使用 System.out,仅在程序将退出的错误情况下使用 System.err。 它将减少交错的可能性。

  • 如果最后一个想法不合适,因为清楚地区分两种输出很重要,请使用允许精确跟踪信息的日志库(Logback 或 Log4j2,最好在 facade 中带有 SLF4J) (日期、时间、严重级别...)并按照程序流时间线的实际顺序阅读它们。


这是仅使用 System.out 的相同代码:
我为输出的预期顺序添加了 //1//2、...注释。

public class TestThread {

    public static void main(final String[] arguments) throws InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        try {
            executor.submit(new Task());
            System.out.println("Shutdown executor"); // 1
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            System.out.println("tasks interrupted"); // not invoked because caught in Task.run()
        } finally {

            if (!executor.isTerminated()) {
                System.out.println("cancel non-finished tasks"); // 3
            }
            executor.shutdownNow();
            System.out.println("shutdown finished"); // 4
        }
    }

    static class Task implements Runnable {

        public void run() {

            try {
                int duration = 6;
                System.out.println("Running Task!");  // 2
                TimeUnit.SECONDS.sleep(duration);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出现在符合预期,无论程序在何处都应该相同 运行:命令行,IDE,...

Shutdown executor

Running Task!

cancel non-finished tasks

shutdown finished

java.lang.InterruptedException: sleep interrupted

注意:

java.lang.InterruptedException: sleep interrupted

的顺序可能仍然不同,因为它是由 Throwable.printStackTrace() 编写的,它依赖于 system.err