将任务移到单独的线程中,避免在任务运行时重复执行任务,以及正确使用执行器和Future

时间:2019-02-01 14:52:13

标签: java future executorservice java.util.concurrent

我们在旧代码和当前代码之间有一个粘合组件。本质上,整个旧版应用程序是单线程的,并且存在可怕的问题,单个指令的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库),并且避免多次执行该任务,即使update方法被调用了太多次(即100) %超出了我的控制范围)。我认为future.isDone()有用,但不能100%

2 个答案:

答案 0 :(得分:1)

executor.submit()确实在新线程中启动任务,但是在executor.awaitTermination(10, TimeUnit.SECONDS); current 线程中等待任务完成之后。无需在 current 线程中等待,但是确实需要一种方法来确定任务是否已在运行。

混乱的部分每次都会创建ExecutorService-无需每次都重新创建它。它可以是该类的实例变量并可以重复使用。理想情况下,它将通过构造函数注入,因此创建它的类可以将其关闭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);
    }
}

答案 1 :(得分:1)

如果您使用的是Java 8或更高版本,这是一个更好的选择

CompletableFuture.runAsync(task);

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