У меня есть объект Task с отношением «один ко многим» к объекту подзадачи. Оба объекта имеют поля состояния. Статус подзадачи в объекте задачи может обновляться одновременно в параллельных потоках. В конце обновления подзадачи проверяется состояние другой подзадачи в том же объекте задачи. Если все выполнено, статус задачи помечается как завершенный. Обновление базы данных происходит после выхода из метода обновления подзадачи.
public void updateSubTask(SubTask subTask, Status status) {
subTask.setStatus(status);
//check all subtask status
if (Status.Completed.equals(status) {
boolean allCompleted = true;
for(SubTask otherTask : subTask.getParentTask().getSubTasks()){
if (!otherTask.equals(subTask) && !Status.Completed.equals(otherTask.getStatus)) {
allCompleted = false;
break;
}
}
if (allCompleted) {
updateParentTask(subTask.getParentTask(), Status.Completed);
}
}
}
Предположим, что в задаче А есть две подзадачи, назовем их подзадачей 1 и подзадачей 2.
1. Метод обновления вызывается потоком А для подзадачи 1.
2. Метод обновления вызывается потоком Б для подзадачи 2.
3. Поток А обновляет подзадачу 1 до "завершена" и проверяет состояние подзадачи 2
4. Поток Б обновляет подзадачу 2 до "завершена" и проверяет статус подзадачи 1
5. Оба потока A и B считают подзадачи 2 и 1 соответственно "незавершенными", поэтому они выходят из метода, не обновляя задачу A до "завершенной"
Теперь у меня есть задача А, которая еще не завершена, но все подзадачи выполнены.
В качестве временного решения я использую одноэлементный объект, содержащий хэш-карту. Каждый раз, когда поток начинает обработку подзадачи, сначала проверяется хэш-карта, если она содержит идентификатор задачи основной задачи подзадачи. Если да. Я усыпляю этот поток на несколько секунд и пытаюсь проверить снова. Если его нет в хэш-карте, то этот идентификатор помещается в хэш-карту, и обработка продолжается. После обработки идентификатор удаляется из хэш-карты.
На данный момент это работает нормально, но использование метода Thread.sleep не кажется элегантным способом приостановить поток. Синхронизированные блоки здесь не подходят, потому что я должен разрешить другим потокам обновлять другие подзадачи, учитывая, что эти подзадачи находятся в другой основной задаче.
Есть ли что-то, что я мог бы использовать в библиотеке java.concurrent (или любой другой библиотеке в этом отношении), которая имеет механизм «замок и ключ», аналогичный тому, что я описал?