首页 > 解决方案 > 等待异步任务的安全有效的方法

问题描述

在系统中,我有一个对象 - 我们称之为TaskProcessor。它保存任务队列,这些任务由某个线程池(ExecutorService+ PriorityBlockingQueue)执行。每个任务的结果以某个唯一标识符保存在数据库中。

知道这个唯一标识符的用户可以检查这个任务的结果。结果可能在数据库中,但任务仍可能在队列中等待执行。在那种情况下,UserThread应该等到任务完成。

此外,以下假设是有效的:

我的第一次尝试(天真的)是在循环中检查结果并休眠一段时间:

// UserThread
while(!checkResultIsInDatabase(uniqueIdentifier))
  sleep(someTime)

但我不喜欢它。首先,我在浪费数据库连接。此外,如果任务在睡眠后立即完成,那么即使结果刚刚出现,用户也会等待。

下一次尝试基于等待/通知:

//UserThread 
while (!checkResultIsInDatabase())
  taskProcessor.wait()

//TaskProcessor
... some complicated calculations
this.notifyAll()

但我也不喜欢。如果更多UserThreads使用TaskProcessor,那么每次完成某些任务时它们都会被不必要地唤醒,而且 - 它们会进行不必要的数据库调用。

最后一次尝试是基于我所说的waitingRoom

//UserThread
Object mutex = new Object();
taskProcessor.addToWaitingRoom(uniqueIdentifier, mutex)
while (!checkResultIsInDatabase())
  mutex.wait()

//TaskProcessor
... Some complicated calculations
if (uniqueIdentifierExistInWaitingRoom(taskUniqueIdentifier))
  getMutexFromWaitingRoom(taskUniqueIdentifier).notify()

但这似乎并不安全。在数据库检查和 之间wait(),可以完成任务(由于尚未调用,因此notify()不会生效),这可能会导致死锁。UserThreadwait()

看来,我应该在某个地方同步它。但我担心它不会有效。有没有办法纠正我的任何尝试,使它们安全有效?或者也许还有其他更好的方法来做到这一点?

标签: javamultithreadingconcurrency

解决方案


您似乎正在寻找某种未来/承诺抽象。看看CompletableFuture,它从 Java 8 开始可用。

CompletableFuture<Void> future = CompletableFuture.runAsync(db::yourExpensiveOperation, executor);

// best approach: attach some callback to run when the future is complete, and handle any errors
future.thenRun(this::onSuccess)
        .exceptionally(ex -> logger.error("err", ex));

// if you really need the current thread to block, waiting for the async result:
future.join(); // blocking! returns the result when complete or throws a CompletionException on error

您还可以从异步操作中返回一个(有意义的)值并将结果传递给回调。要使用此功能,请查看supplyAsync()thenAccept()thenApply()whenComplete()

您还可以将多个期货组合成一个甚至更多。


推荐阅读