我有这些类,用于创建要在运行时存储的对象
Class Person
String name
Pet[] pets
Class Pet
String name
Person[] owners
boolean neutered
一开始我使用这些HashMap来存储它们
HashMap people
HashMap pets
但是我想让实现并发,所以我像这样更改了这些映射
ConcurrentHashMap people
ConcurrentHashMap pets
我使用“compareAndSet in a while循环”模式
来进行原子更新。
但我仍然遇到问题,因为我的 People
map 中的每个人在 Pets
map 中都有关联的宠物。为了保持更新原子性,我添加了 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
}
}
现在我有了允许并发读取和同步写入的运行时存储。我的问题是是否有一种方法可以使写入同时进行,同时保护人与宠物之间的关系。
最佳答案
您可以在 People
人物对象上进行同步,而不是在皮带对象上进行同步。这允许对不同的人及其宠物同时进行更改,同时阻止对一个人和她的宠物同时进行更改。
PS,从表面上看,你的锁定系统似乎有点过于复杂。假设人 - 宠物是一对多的关系,一个人可以有很多宠物,但任何宠物只有一个主人,只需要在人对象上同步就可以了。
PS2,命名很重要,你的类名是复数,我认为使用 Person
和 Pet
而不是 People
和 Pets
将更好地描述概念,使您的代码更易于理解。
编辑
像 neuter
这样的方法只接受宠物而不需要更改所有者的数据,为了使它们并发,需要在宠物上同步,但这意味着:
- 当您编辑人和她的宠物时,您需要同步人和宠物,以防止仅更改宠物
- 有时,宠物可以被锁定,而养宠物的人也需要被锁定
当一个线程拥有宠物锁并尝试获取人员锁,而另一个线程拥有人员锁并尝试获取宠物锁时,上述情况可能会导致死锁情况。我的解决方案是在所有者上同步,即使只需要更改宠物,这意味着 changePetNames 和 neuter 看起来像:
changePetNames(Person person, Pets[] pets, String[] names) {
synchronized(person) {
// make updates
}
}
neuter(Pets[] pets) {
for (Pets pet: pets) {
// make sure pets owner exists
synchronized(pet.getOwner()) {
// make updates
}
}
}
这样,如果您从不将同步操作嵌套在不同的人身上,就不会发生死锁。
编辑2 当主人与宠物是多对多关系时,您需要同步人和宠物的独特组合的表示,这将重现您已经获取的更新写入锁。我的结论是,如果可以确保不会发生死锁,则不需要额外的同步租约。
如果两个线程想要获取另一个线程之前已获取过的锁,就会发生死锁,因此如果您可以确保始终以相同的顺序获取锁,则不会出现此问题。
如果您向人和宠物添加唯一的创建 ID,并始终按升序获取每个更新集的锁,则不会发生死锁情况:
changePetNames(Person person, Pets[] pets, String[] names) {
// sort Person ID's
// get Person lock in ID sequence
// sort Pet ID's
// get Pet locks in ID sequence
// make updates
// release locks
}
应该可以解决问题。
关于java - 复合编辑的并发性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1966293/