(仅供参考,我无法发布有问题的实际代码)
我有一个界面
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
...
}
}
}
虽然我认为这可行,但锁定方法参数似乎很危险。据我所知,该线程中的问题与此不同。那么这可能是有效的用法吗?
部分原因是该实现在某种程度上试图实现接口不完全支持的功能。即接口无法提供锁定内部字段的方法,但实现类型需要这样做。在一定程度上重新设计是可能的。其他一些要点:
示例:待定