Ant*_*tiz 4 java deadlock locking synchronized
我有以下代码:注意:为了便于阅读,我尽可能地简化了代码.如果我忘了任何关键的部分让我知道.
public class User(){
private Relations relations;
public User(){
relations = new Relations(this);
}
public getRelations(){
return relations;
}
}
public class Relations(){
private User user;
public Relations(User user){
this.user = user;
}
public synchronized void setRelation(User user2){
Relations relations2 = user2.getRelations();
synchronized(relations2){
storeRelation(user2);
if(!relations2.hasRelation(user))
relations2.setRelation(user);
}
}
public synchronized boolean hasRelation(User user2){
... // Checks if this relation is present in some kind of collection
}
/*Store this relation, unless it is already present*/
private void storeRelation(User user2){
... // Stores this relation in some kind of collection
}
}
Run Code Online (Sandbox Code Playgroud)
这个实现应该确保所有关系x,y与:
x.user = u_x
y.user = u_y
Run Code Online (Sandbox Code Playgroud)
以下不变量持有:
x.hasRelation( u_y ) <=> y.hasRelation( u_x )
Run Code Online (Sandbox Code Playgroud)
我认为上述代码是否成立?
注意:它当然不会在执行setRelation(..)期间保持,但是在那一刻,所涉及的两个关系的锁都由执行线程保持,因此没有其他线程可以读取其中一个的hasRelation(..)涉及的关系.
假设存在这种情况,我相信仍存在潜在的死锁风险.那是对的吗?如果是,我该如何解决?我想我需要以某种方式原子地获取setRelation(..)中的两个锁.
你在两点上都是正确的:你的不变量确实成立(假设我正确理解你的方法名称是什么意思等等,并假设if(!relations.hasRelation(user)) relations2.setRelation(user2);你打算写if(!relations2.hasRelation(user)) relations2.setRelation(user);),但你确实存在死锁的风险:如果一个线程需要获取锁定x然后再打开y,另一个线程需要获取锁定y然后再打开x,然后存在每个线程成功获得其第一次锁定的风险,从而阻止另一个线程获得其第二次锁定.
一种解决方案是强制执行严格的通用排序以获取Relations实例上的锁.你要做的是,你添加一个常量整数字段lockOrder:
private final int lockOrder;
Run Code Online (Sandbox Code Playgroud)
和一个静态整数字段currentLockOrder:
private static int currentLockOrder = 0;
Run Code Online (Sandbox Code Playgroud)
每次创建Relations实例时,都将其设置lockOrder为当前值currentLockOrder,并递增说:
public Relations()
{
synchronized(Relations.class) // a lock on currentLockOrder
{
lockOrder = currentLockOrder;
++currentLockOrder;
}
}
Run Code Online (Sandbox Code Playgroud)
这样每个实例Relations都会有一个独特的,不可变的值lockOrder.setRelation然后,您的方法将按指定的顺序获取锁:
public void setRelation(final User thatUser)
{
final Relations that = thatUser.getRelations();
synchronized(lockOrder < that.lockOrder ? this : that)
{
synchronized(lockOrder < that.lockOrder ? that : this)
{
storeRelation(thatUser);
if(! that.hasRelation(user))
that.storeRelation(user);
}
}
}
Run Code Online (Sandbox Code Playgroud)
从而确保如果两个线程都需要获得两个锁x和y上时,无论他们都会先获取锁x,或者他们都会第一个GET锁上y.无论哪种方式,都不会发生死锁.
请注意,顺便说一下,我变setRelation来storeRelation.setRelation会工作,但为什么要增加这种复杂性?
此外,还有一件事我没有得到:如何无条件地x.setRelation(u_y)召唤,但只有在没有关系的情况下才打电话(或)?这没有意义.似乎需要两个检查,或者两个检查都不是.(没有看到实施,我无法猜测其中的哪一个.)x.storeRelation(u_y) y.setRelation(u_x)y.storeRelation(u_x)yRelations.storeRelation(...)
| 归档时间: |
|
| 查看次数: |
2418 次 |
| 最近记录: |