我们在遗留代码和当前代码之间有一个粘合组件。从本质上讲,整个遗留应用程序都是单线程的,并且存在可怕的问题,单个指令的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秒后,很明显,它正在将整个当前线程发送到睡眠状态。
我该如何将我的taks放在一个全新的线程上(使用concurrency
库),并避免多次执行任务,即使更新方法被调用了太多次(这100%超出了我的控制范围)。我认为future.isDone()
是可行的,但不是100%
如果您使用的是Java 8或更高版本,那么做会更好
CompletableFuture.runAsync(task);
因为这将在由JVM管理的Fork连接线程池上执行,您不会因为创建或关闭它而担心自己。当然,这将异步运行,这符合您的要求。
executor.submit()
确实在新线程中启动任务,但紧接着executor.awaitTermination(10, TimeUnit.SECONDS);
在当前线程中等待任务完成。不需要在当前线程中等待,但确实需要有一种方法来确定任务是否已经在运行。
混乱的部分是每次都创建ExecutorService
——没有必要每次都重新创建它。它可以是类的实例变量,并可以重复使用。理想情况下,它将通过构造函数注入,这样创建它的类就可以在真正需要的时候关闭它。
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);
}
}