用于复合编辑的java并发性
我有这些类,用于创建要在运行时存储的对象
Class Person
String name
Pet[] pets
Class Pet
String name
Person[] owners
boolean neutered
起初,我使用这些哈希映射来存储它们
HashMap people
HashMap pets
但是我想让实现并发,所以我像这样修改了这些映射
ConcurrentHashMap people
ConcurrentHashMap pets
我使用"compareAndSet in a while loop" pattern
来进行原子更新
但是我仍然有一个问题,因为我的People
地图中的每个人都在Pets
地图中关联了宠物。为了保持原子更新,我添加了ReentrantReadWriteLocks
,这样我就可以同时更新People
对象和关联的Pet
对象
ConcurrentHashMap people
ConcurrentHashMap peopleLocks
ConcurrentHashMap pets
ConcurrentHashMap petLocks
现在,当我对多个记录执行编辑时,我首先获取所有写锁,然后进行编辑,最后释放写锁。这样可以确保在我进行更新时不会阅读
changePetNames(Person person, Pets[] pets, String[] names) {
// get Person lock
// get Pet locks
// make updates
// release locks
}
neuter(Pets[] pets) {
// get Pet locks
// make updates
// release locks
然后我让我所有的编辑方法在一个对象上同步,这样竞争编辑就不会死锁
private final Object leash = new Object();
changePetNames(Person person, Pets[] pets, String[] names) {
synchronized(leash) {
// get Person lock
// get Pet locks
// make updates
// release locks
}
}
neuter(Pets[] pets) {
synchronized(leash) {
// get Pet locks
// make updates
// release locks
}
}
所以现在我有了运行时存储,它允许并发读取和同步写入。我的问题是,在保护人与宠物之间的关系的同时,是否有一种方法可以使书写同时进行
# 1 楼答案
您可以在
People
person对象上同步,而不是在leash对象上同步。这允许同时更改不同的人及其宠物,同时阻止同时更改一个人及其宠物另外,从外观上看,你的锁定系统似乎有点过于复杂。假设人与宠物是一对多的关系,一个人可以有很多宠物,但是任何宠物只有一个主人,你只需要在person对象上同步
PS2,命名很重要,而且类名是复数的,我认为使用
Person
和Pet
而不是People
和Pets
可以更好地描述这些概念,使代码更容易理解编辑 像
neuter
这样的方法只接受宠物而不需要更改所有者中的数据,为了使它们并发,可以在宠物上同步,但这意味着:当一个线程有一个pet锁并试图获取person锁,而另一个线程有person锁并试图获取pet锁时,上述情况可能会导致死锁。我的解决方案是在主人身上进行同步,即使只需要更改宠物,这意味着更改宠物名称和绝育将如下所示:
这样,如果您从不在不同的人上嵌套同步操作,就不会发生死锁
Edit2 当主人与宠物是一种多对多关系时,您需要在个人和宠物的独特组合的表示上进行同步,这将复制您已经为更新获取的写锁。我的结论是,如果可以确保不会发生死锁,那么就不需要额外的同步租约
如果两个线程想要获得另一个线程之前已获得的锁,则会发生死锁,因此如果您可以确保始终以相同的顺序获得锁,则不会出现此问题
如果要始终按递增顺序为每个更新集的Person锁、Pet锁和aquire锁添加唯一的创建ID,则不会出现死锁情况:
我们应该做到这一点
# 2 楼答案
现在所有编辑都“相互锁定”。这在什么方面是一种进步
我不明白为什么你不能简单地忽略同步化。在Person上获得读写锁已经可以防止冲突的并发更新(除非宠物更改了所有者,这可以通过对原始所有者进行额外的同步处理来解决)
编辑:啊,“相互锁定”表示“死锁”
Wikipedia's article on the Dining Philosophers problem讨论了几种避免死锁的典型技术。在您的情况下,最简单的解决方案可能是“资源层次结构解决方案”,其中包含要同步到的对象的总顺序。例如,如果您按照递增(唯一)id的顺序锁定对象,然后执行该操作,然后释放所有锁,则不会发生死锁。(因为持有“最高”锁的线程不会受到任何其他线程的阻碍。)