将任务移动到单独的线程,避免重复任务 运行 并正确使用 Executors + Future

Moving a task to a separate thread, avoid duplication while task running & proper use of Executors + Future

我们在遗留代码和当前代码之间有一个粘合组件。本质上,整个遗留应用程序都是单线程的,并且存在可怕的问题,其中 ui 刷新单个指令可能会发生 5 到 8 次。

我想在第一个更新请求发生 +2 秒后发布异步消息。

我们不要纠结于为什么,这不是我真正想做的,但我必须至少了解如何做到这一点,这样我才能实施真正的解决方案。

Runnable task = () -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        messageBus.publishAsynch(new LegacyUiUpdateEvent());
    } catch (InterruptedException e) {
        // TODO Log something
        Thread.currentThread().interrupt();
    }
};

@Override
public void update(Observable arg0, Object arg1) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
            executor.shutdownNow();
        } catch (InterruptedException e) {
            // TODO Log something
            Thread.currentThread().interrupt();
        }
    }
}

理论是:如果未来的任务不存在,我们创建它,一旦它在那里,如果它没有完成(因为这是错误的遗留更新 4/x,其中x ∈ [5,12] 并且睡眠仍然有效)那么我们完全跳过并且不创建新的执行者。

问题是 ,据我所知,executor.submit(task) 实际上并没有发生在新的轨道上。就像我说的遗留应用程序是单线程的,在我将睡眠时间增加到 15 秒后,很明显它正在将整个当前线程发送到睡眠状态。

我如何将我的任务放在一个全新的线程上(使用 concurrency 库)并避免多次执行该任务,即使更新方法被调用了太多次(并且100% 不受我控制)。我认为 future.isDone() 有效,但不是 100%

executor.submit() 确实在新线程中启动任务,但紧接着 executor.awaitTermination(10, TimeUnit.SECONDS); 当前 线程中等待任务完成。不需要在 current 线程中等待,但是确实需要有一种方法来确定任务是否已经 运行.

麻烦的部分是每次都创建 ExecutorService - 没有必要每次都重新创建它。它可以作为 class 的实例变量并重新使用。理想情况下,它将通过构造函数注入,因此创建它的 class 可以将其关闭 if that's really needed.

 private final ExecutorService executor = Executors.newSingleThreadExecutor();  // or injected through constructor
 private Future<?> futureTask;

@Override
public void update(Observable arg0, Object arg1) {
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
    }
}

如果您的 Java 8 或更高,这是更好的做法

CompletableFuture.runAsync(task);

因为这将在由 JVM 管理的 Fork-join 线程池上执行,您不必担心创建它或关闭它。当然,这将 运行 异步地满足您的要求。