使用预定义密钥锁定的竞争条件解决方案

时间:2017-01-13 07:11:03

标签: java multithreading asynchronous concurrency race-condition

我有一个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);
        }
    }
}

让我们说任务A下有两个子任务,称之为子任务1和子任务2 1.线程A为子任务1调用更新方法。
2.线程B为子任务2调用更新方法。
3.线程A将子任务1更新为"已完成"并检查子任务2的状态 4.线程B将子任务2更新为"已完成"并检查子任务1的状态 5.线程A和B分别将子任务2和1视为"未完成",因此它们退出方法而不将任务A更新为"已完成"

我现在最终得到的任务A尚未完成,但所有子任务都已完成。

作为临时解决方案,我使用的是包含hashmap的单例对象。每次线程开始处理子任务时,如果哈希图包含子任务的主任务的任务ID,则首先检查该哈希图。如是。我把那个线程睡了几秒钟然后再次检查。如果它不在hashmap中,那么该ID将被放置在hashmap中并继续处理。处理完成后,ID将从hashmap中删除。

此时此工作正常,但使用Thread.sleep方法似乎不是暂停线程的优雅方式。同步块不是一个选项,因为我应该允许其他线程更新其他子任务,因为这些子任务在不同的主要任务下。

我是否可以在java.concurrent库(或任何其他库)中使用"锁和键"机制与我所描述的相似?

2 个答案:

答案 0 :(得分:1)

简单回答:observer pattern;可能是基于你好老PropertyChangeListener

“子任务”既不应该担心其“父”任务的状态,也不应该担心其他“子任务”的状态。

对此的规范回答是“父母”任务需要“倾听”其子女的行为。

答案 1 :(得分:0)

在我看来,你的设计不是最佳的。

我要做的是让任务监视子任务中的事件(监听任务完成)。

您需要的是同步两个异步子任务。这样就可以在Task中进行同步。 您可以使用AtomicInteger(甚至是布尔值)来了解何时完成了两个子任务。

所以任务工作将是

  • 将侦听器附加到子任务
  • 开始执行subtask1。
  • 开始执行subtask2。
  • 当子任务完成时,它会通知听众(任务)。
  • 任务收到通知并将其状态更改为1/2已完成。
  • 当2/2完成后,触发数据库更改或您需要做的任何事情。