需要同步访问仅通过接口公开的两个不同集合

问题描述 投票:0回答:1

(仅供参考,我无法发布有问题的实际代码)

我有一个界面

interface IEntity {
    List<IEntity > getChildren(); //return list of children, safe to iterate i.e. a copy
    List<IEntity > addChild(IEntity child); //add a child
    List<IEntity > getParents(); //return list of parents, safe to iterate i.e. a copy
    List<IEntity > addParent(IEntity parent); //add a parent
}

要求父/子列表保持同步。因此,每次调用

addChild 
时,该方法的内部需要确保子集合的父集合也被更新。潜在的无限循环(addChild -> addParent -> addChild)可以通过在尝试更新相应的父/子之前更新内部集合来解决。

非线程安全代码相对简单。然而,一旦考虑线程安全,它就会变得更加复杂。我相信如果每个方法只是锁定它自己的集合,就会产生死锁情况。例如

addChild(IEntity child) {
    synchronized(children) {
        if(!children.contains(child)) {
            ...
            child.addParent(this);
            ....
        }
    }
}
...
addParent(IEntity parent) {
    synchronized(parents) {
        if(!parents.contains(child)) {
             ...
             parent.addChild(this)
             ...
        }
    }

每个实例都锁定其内部集合,上述循环(addChild -> addParent -> addChild)会导致死锁,因为需要在

contains
测试之前获取锁。

看起来

addChild
addParent
这两个方法的内部都需要锁定另一个实体对应的集合,这样我们就可以保证当方法退出时集合是同步的。但是,这两个实例都无法直接访问内部集合。
IEntity 
接口类型的所有参数。

我考虑锁定实例本身,例如...

addChild(IEntity child) {
    synchronized(this) { //Always lock parent first to prevent deadlocks
        synchronized(child) { //Generates a warning on locking method parameters
        ....
        }
    }
}

addParent(IEntity parent) {
    synchronized (parent) { //Always lock parent first to prevent deadlocks
        synchronized (this) { //Generates a warning on locking method parameters
        ...
        }
    }
}

虽然我认为这可行,但锁定方法参数似乎很危险。据我所知,该线程中的问题与此不同。那么这可能是有效的用法吗?

部分原因是该实现在某种程度上试图实现接口不完全支持的功能。即接口无法提供锁定内部字段的方法,但实现类型需要这样做。在一定程度上重新设计是可能的。

其他一些要点:

    这些不是数据库实体,因此我无法将该同步委托给任何其他组件
  • 这些实体确实会有其他实现,因此需要使用该接口。
java synchronization thread-safety
1个回答
0
投票
一种解决方案是将参数转换为实现,然后锁定。

示例:待定

© www.soinside.com 2019 - 2024. All rights reserved.