我有一个假设情景如下:
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表上获取独占锁。
答案 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个解决方案之外,您还可以查看AKKA
或ZOOKEEPER
等同步/协调框架,以实现流程之间的协调和同步。
出于好奇,为什么那个孩子在你的案子中出现在父母面前?