在玩具示例中避免竞争条件

时间:2017-10-27 02:54:47

标签: java multithreading

我有一个假设情景如下:

class Foo {
   int parentId;
   String data;
}

我有一个处理流/ List<Foo>对象的多线程应用程序,应用程序的目的是检查数据库中是否存在每个Foo的parentId,如果不存在,则创建一个Parent对象并存储它在父表设置的DB表中。

问题是当两个具有相同parentId(Foo = A.parentId)的B.parentId个对象(A和B)出现时。在处理过程中,A会检查A.parentId是否存在 - &gt;它没有 - &gt;使用parentId = A.parentID创建一个新的Parent对象。但是,B已经检查了B.parentId是否存在(在A可以完成创建新的Parent对象之前),B也得出相同的结论,并创建了一个父parentId = B.parentId的新父对象。

现在,我最终有两个Parent对象,数据库中的parentId完全相同。我知道我可以设置约束以避免重复,但我试图了解是否有标准解决方案来解决这些问题(竞争条件?)。还假设我无法在db表上获取独占锁。

3 个答案:

答案 0 :(得分:0)

您应该添加一个synchronized方法或同步代码块来包装您的代码逻辑,检查parentId是否存在并创建父对象。

如果父对象是唯一的,另一种避免创建两个重复父对象的方法是在parentId列上创建表的唯一索引

答案 1 :(得分:0)

如果只有一个多线程应用程序实例,我希望ConcurrentHashMap <Integer,CountDownLatch>只允许一个线程插入parentId。

ConcurrentHashMap<Integer, CountDownLatch> tmp = new ConcurrentHashMap<>();// global variable in the process class

public void process(Foo f) {
            //db op: test if parent id exists
            if (f.parentId not exists) {
                CountDownLatch a = tmp.get(f.parentId);
                //no one update parent id
                if (a == null) {
                    a = new CountDownLatch(1);
                    CountDownLatch old = tmp.putIfAbsent(f.parentId, a);
                    //no one update exactly
                    if (old == null) {
                        insert parent id
                        a.countDown();
                        //tmp.remove(f.parentId);//line 1
                        process f
                    } else {
                        old.await();//here may throw interrupted exception;you may retry or just ignore according to your application.
                        process f
                    }

                }
            }
    }

在上面的代码中,如果我们不删除在父id插入db后永远不会使用的CountDownLatch实例,tmp会增长(内存泄漏)。第1行中的代码就是我说的。但是如果我只是删除它与第1行一样,有可能一个检查父ID不存在并且得到CountDownLatch返回null,但实际上父ID被插入到db中,因为我们删除了其他线程中的CountDownLatch。我不知道如何处理这种情况。

如果有很多实例,我会在所有这些实例之前安装一个调度程序,将同一个parentId调度到同一个实例。例如,三个实例用1,2,3表示;当Foo进来时,我会检查它的parentId并决定哪个实例将处理这个Foo,就像负载均衡器一样。

答案 2 :(得分:-2)

除了您在问题中发布的2个解决方案之外,您还可以查看AKKAZOOKEEPER等同步/协调框架,以实现流程之间的协调和同步。

出于好奇,为什么那个孩子在你的案子中出现在父母面前?