java - 复合编辑的并发性

标签 java concurrency

我有这些类,用于创建要在运行时存储的对象

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,命名很重要,你的类名是复数,我认为使用 PersonPet 而不是 PeoplePets 将更好地描述概念,使您的代码更易于理解。

编辑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/

相关文章:

java - 程序顺序规则在构造函数中起作用之前是否发生?

java - 根据 Java 中的表格分配成绩

objective-c - 使用 NSOperationQueues 的核心数据和并发

javascript - 在 Web Worker 中发起 HTTP 请求

java - 重新请求项目的 Google Drive 权限失败

java - 给定数据库结构可以在运行时更改,如何处理并发 SQL 更新

go - Go 中的缓冲 channel

java - 我在哪里可以找到一些很好的例子来学习线程的基础知识?

java - 使用 Java 邮件排队或不排队

java - Java 中的列表、链表、数组列表