ExecutorService.execute() 静默退出 运行() 方法

ExecutorService.execute() silently exits run() method

尝试测试我的 classes 之一是否可以处理在多个线程上的访问。我有一个 JUnit 测试,我在其中创建了一个 class 来实现 Runnable 和 运行 我的 class.

当我 运行 执行时,它到达 运行() 方法中的一行并在该线程上退出而不报告任何问题,我用 Try-Catch( Throwable),但仍然没有出现问题的迹象。

这是我的代码:

class ConcurrencyTests {
    class ConcurrentComponentTracker implements Runnable {
        private String component;

        ConcurrentComponentTracker(String component) {
            this.component = component;
       }

       @Override
       public void run() {
           try {
           System.out.printf("Component to track: [%s]\n", component);
           ParserHandler parserHandler = new ParserHandler();
           System.out.println(parserHandler.componentTracker(component));
           }
           catch (Throwable t) {
               t.printStackTrace();
           }
       }
   }

    @Test
    void runRunner() {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        String[] componentsToTrack = {"x", "y", "z"};

        executor.execute(new ConcurrentComponentTracker(componentsToTrack[0]));
        executor.execute(new ConcurrentComponentTracker(componentsToTrack[1]));
        executor.execute(new ConcurrentComponentTracker(componentsToTrack[2]));
    }
}

并且输出:

Component to track: [x]

Component to track: [y]

Component to track: [z]

它似乎只是在 ParserHandler 实例化行上退出而没有报告任何内容。当尝试调试并进入该行时,它只是跳到最后而没有让我检查 ParserHandler class 实例化。

谁在评论中回答然后删除它就解决了我的问题。我的主 JUnit 线程没有等待其他线程完成。所以我将这些行添加到最后,它按预期工作:

executor.shutdown();

try {
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
catch (InterruptedException e) {
    e.printStackTrace();
}

JUnit 测试应该实际测试和验证结果。在这种特殊情况下,您的测试产生的输出应该由 developer/tester 验证,因此这不是自动化。 我建议您使用 Future<?> 来跟踪任务执行的状态。对于 Runnable 任务 get() 如果任务执行成功,Future<?> 方法将 return null,否则它会向您转发用 ExecutionException 包装的异常。代码将如下所示:

@Test
void runRunner() {
    ExecutorService executor = Executors.newFixedThreadPool(3);
    String[] componentsToTrack = {"x", "y", "z"};

    assertNull(executor.submit(new ConcurrentComponentTracker(componentsToTrack[0]).get());
    assertNull(executor.submit(new ConcurrentComponentTracker(componentsToTrack[1]).get());
    assertNull(executor.submit(new ConcurrentComponentTracker(componentsToTrack[2]).get());

    // cleanup and shutdown pool
    executor.shutdown();
    try {
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    } catch (InterruptedException e) {
        // ignore this
    }
}

在这样做之前,您应该删除 run() 方法中的 try/catch 块。

您可能可以改进的另一件事是使执行程序成为测试的一个字段 class 并使用传统的 JUnit 生命周期管理它,使用 @BeforeClass@Before、... 来初始化,在测试之间清理,优雅地关闭池。请记住,从资源的角度来看,线程池并不便宜。

希望对您有所帮助!

P.S. 如果你的目标是并行处理你的 ConcurrentComponentTracker 你可以将 Future<?> 存储在一些集合中并且 运行 提交所有任务后的断言。