有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用于复合编辑的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
    }
}

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


共 (2) 个答案

  1. # 1 楼答案

    您可以在Peopleperson对象上同步,而不是在leash对象上同步。这允许同时更改不同的人及其宠物,同时阻止同时更改一个人及其宠物

    另外,从外观上看,你的锁定系统似乎有点过于复杂。假设人与宠物是一对多的关系,一个人可以有很多宠物,但是任何宠物只有一个主人,你只需要在person对象上同步

    PS2,命名很重要,而且类名是复数的,我认为使用PersonPet而不是PeoplePets可以更好地描述这些概念,使代码更容易理解

    编辑neuter这样的方法只接受宠物而不需要更改所有者中的数据,为了使它们并发,可以在宠物上同步,但这意味着:

    • 当你编辑一个人和她的宠物时,你需要在这个人和宠物上同步,以防止只对宠物进行更改
    • 有时一只宠物可以被锁上,而带着宠物的人也需要被锁上

    当一个线程有一个pet锁并试图获取person锁,而另一个线程有person锁并试图获取pet锁时,上述情况可能会导致死锁。我的解决方案是在主人身上进行同步,即使只需要更改宠物,这意味着更改宠物名称和绝育将如下所示:

    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 当主人与宠物是一种多对多关系时,您需要在个人和宠物的独特组合的表示上进行同步,这将复制您已经为更新获取的写锁。我的结论是,如果可以确保不会发生死锁,那么就不需要额外的同步租约

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

    如果要始终按递增顺序为每个更新集的Person锁、Pet锁和aquire锁添加唯一的创建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
    }
    

    我们应该做到这一点

  2. # 2 楼答案

    I then had all my edit methods synchronize on one object, so that competing edits wouldn't lock each other out.

    现在所有编辑都“相互锁定”。这在什么方面是一种进步

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

    编辑:啊,“相互锁定”表示“死锁”

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