我们在旧代码和当前代码之间有一个粘合组件。本质上,整个旧版应用程序是单线程的,并且存在可怕的问题,单个指令的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%
答案 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线程池上执行,因此您不必担心创建或关闭它。当然,它会异步运行,从而满足您的要求。