2009-12-27 60 views
2

我有这些类,我用它来创建,我想在运行时并发复方编辑

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 loop" pattern进行原子更新。

但我仍然有一个问题,因为我的People地图中的每个人都有Pets地图中的相关宠物。为了保持更新原子我加ReentrantReadWriteLocks,这样我可以同时更新与相关Pet对象People对象。

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 
    } 
} 

所以现在我有运行的存储,允许并发读取和写入同步。我的问题是,是否有办法使写入同时进行,同时保护人与宠物之间的关系。

+0

谢谢,meriton。我重新陷入僵局。 – Rapier 2009-12-27 18:53:02

回答

2

代替皮带对象上同步的,你可以在People人对象上同步。这可以同时改变不同的人和他们的宠物,同时阻止一个人和她的宠物的同时变化。

PS,从外观上来看你的锁定系统似乎有点过于复杂。在假设的人 - 宠物是1对多的关系,一个人可以有很多宠物,但任何宠物只有一个所有者,只有synchonizing人物对象上可能是你需要的一切。

PS2,命名是很重要的,你的类名是多元的,我想用PersonPet代替PeoplePets会更好地描述使你的代码更容易理解的概念。

编辑 方法,如neuter,仅拍摄宠物,而无需更改数据的所有者会,让他们并发,对宠物的同步,但是这意味着:

  • 当您编辑一个人和她的宠物,你需要两个人与宠物同步谨防宠物只改变
  • 有时宠物可以被锁定,同时与宠物的人也需要锁定

当一个线程有一个宠物锁并尝试获得一个人锁而另一个线程拥有该人锁并尝试获取宠物锁时,上述情况可能会导致死锁情况。我的解决办法是对业主进行同步,即使只宠物需要改变,这意味着changePetNames和中性会是什么样子:

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 
     } 
    } 
} 

这样没有死锁可能会发生,如果你从来没有嵌套同步动作不同的人。

EDIT2 当业主的宠物是一个多对多的关系,你需要对人员和宠物的独特组合,这将重现您AQUIRE更新已经写锁的represenatation同步。我的结论是,如果可以确保不发生死锁,那么不需要额外的同步租约。

如果两个线程想要获得另一个之前已获得的锁,就会发生死锁,因此如果可以确保锁总是以相同的顺序获得,则此问题不会发生。

如果你想补充一个唯一的ID创作既人与宠物和每次更新中始终递增的顺序设置AQUIRE锁,不能发生死锁的情况:

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 
} 

应该做的伎俩。

+0

我修改了问题以显示事件,尽管人与宠物之间存在某种关系,但某些修改与人员无关。在这种情况下,人们的同步将对只需要访问宠物的修改征求额外的要求。 – Rapier 2009-12-27 17:37:20

+0

您的评论让我意识到我需要人与宠物之间的多对多关系,所以我修改了这个问题。 – Rapier 2009-12-27 18:26:53

1

然后我有我所有的编辑方法之一对象 同步,使 竞争的修改将不会锁定每个 等了。

现在所有的编辑“互相锁定”。这是一种改进?

我不明白你为什么不能简单地忽略那个同步。在Person上获取ReadWrite锁已经可以防止冲突的并发更新(除非宠物更改了所有者,这可以通过对原始所有者进行额外的同步来处理)。

编辑:啊,“对方锁定”是指“僵局” ......

Wikipedia's article on the Dining Philosophers problem讨论了避免死锁的几种典型技术。您的案例中最简单的解决方案可能是“资源层次结构解决方案”,其中的对象的总排序要同步到。例如,如果按升序(唯一)ID的顺序锁定对象,然后执行该操作,然后释放所有锁定,则不会发生死锁。 (因为持有“最高”锁的线程不会受到任何其他线程的阻碍。)

+0

我的编辑例程可以同时抓取多个宠物,有时还会抓取多个人。因此,如果两个编辑线程竞争对象A和B,则有可能一个线程获取A而另一个线程获取B,从而导致它们最终陷入僵局。围绕皮带的同步修复了这一点。 – Rapier 2009-12-27 17:09:38

+0

啊,现在我明白你的意思是“互相锁定”。我编辑了我的答案,以包含更好的并发性的死锁保护方案。 – meriton 2009-12-27 17:40:23

+0

我修改了问题以显示事件,尽管人与宠物之间存在某种关系,但某些修改与人员无关。因此,虽然资源层次结构对changePetNames()起作用,但对于neuter()而言,这成为一个问题。 – Rapier 2009-12-27 17:51:26