В системе у меня есть объект - пусть назовем его TaskProcessor. Он содержит очередь задач, которые выполняются некоторым пулом потоков (ExecutorService + PriorityBlockingQueue). Результат каждой задачи сохраняется в базе данных под некоторым уникальным идентификатором.
Пользователь, который знает этот уникальный идентификатор, может проверить результат этой задачи. Результат может быть в базе данных, но задача также может ожидать выполнения в очереди. В этом случае UserThread должен дождаться завершения задачи.
Кроме того, следующие предположения действительны:
-
Кто-то может поставить задачу в
TaskProcessorа какой-то случайныйUserThreadможет получить доступ к результату, если он знает уникальный идентификатор. -
UserThreadиTaskProcessнаходятся в одном приложении.TaskProcessorсодержит пул потоков, аUserThread- это простоUserThreadThread сервлета. -
UserThreadдолжен быть заблокирован при запросе результата, и результат еще не завершен.UserThreadдолжен быть разблокирован сразу после завершения задачи (или задач)TaskProcessorсгруппированных по уникальному идентификатору
Моей первой попыткой (наивной) было проверить результат в цикле и поспать некоторое время:
// 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() будет неэффективной, поскольку UserThread еще не UserThread wait()), что может привести к взаимоблокировке.
Кажется, мне нужно где-то синхронизировать. Но я боюсь, что это будет не эффективно. Есть ли способ исправить любые мои попытки, сделать их безопасными и эффективными? Или, может быть, есть какой-то другой, лучший способ сделать это?