问题描述
我需要在ConcurrentHashMap
上执行两次并发操作。
我有一个ConcurrentHashMap
中的一个Client
,其中一个Integer id
是 key ;每个客户端都有一个selectedId
属性,该属性包含另一个客户端或其本身的ID(表示没有人选择)。
在每个clientChangedSelection(int whoChangedSelection)
并发事件中,我需要自动检查客户端和所选客户端是否相互引用。如果这样做,它们将被移除并退回。
同时,可以通过其他线程添加或删除客户端。
“理想”的解决方案是为每个条目锁定并锁定受影响的条目,每个clientChangedSelection
在其自己的线程中运行,因此如有必要,它们将等待。当然这是不实际的。最重要的是,据我所知,ConcurrentHashMap
不提供用于手动锁定存储桶的API。最重要的是,我在某处读到了桶的锁不重入。不知道这是真的还是为什么。
我的“想象力”方法大量使用嵌套的compute()
方法来保证原子性。如果ConcurrentHashMap
的锁未重入,则将无法使用。它会失去任何可读性,需要“值捕获”解决方法,并且性能可能很差。但是性能并不是什么大问题,只要它们不影响处理无关条目的线程即可。 (即在不同的存储桶中)。
public Client[] match(int id){
final Client players[]=new Client[]{null,null};
clients.computeIfPresent(id,(idA,playerA)->{
if(playerA.selectedId!=idA){
clients.computeIfPresent(playerA.selectedId,(idB,playerB)->{
if(playerB.selectedId==idA){
players[0]=playerA;
players[1]=playerB;
return null;
}else{
return playerB;
}
});
}
if(players[0]==null){
return playerA;
}else{
return null;
}
});
if(players[0]==null){
return null;
}else{
return players;
}
}
“不可接受”的方法同步整个match
方法。首先,使并发事件无效。
“错误”方法会在与两个客户端一起工作时临时将其删除,并重新添加以防万一。这使得使用条目的并发事件失败而不是等待,因为“使用中”与“不存在”变得难以区分。
我想我将回到一个计时器,该计时器每n秒检查一次整个地图。不需要额外的同步,但是不太优雅。
这或多或少是一种常见的并发情况,但是ConcurrentHashMap
引起了人们的兴趣,它阻止了过多地发明轮子。
您的方法是什么?有什么建议吗?
编辑1
同步每个访问权限(从而使使用 Concurrent HashMap的方法无效)也不可行。必须保留并发访问权限,否则问题本身将不存在。
我已经从selectedId
中删除了match()
参数,但是请注意,这并不重要。虚拟事件clientChangedSelection(int whoChangedSelection)
表示并发事件。可以在任何操作线程中随时发生。 match()
只是一个示例函数,被调用来处理匹配。希望我说得更清楚。
编辑2
这是我最终得到的双重同步功能。 idSelect()
是需要同步的方法示例,因为它会修改客户端属性。在这种情况下,不需要put()
和remove()
进行同步,该函数看到的内容已经足够新了。
碰巧有两个检查:第一个检查只是为了使客户端同步到其上,第二个检查是为了判断先前执行的匹配是否成功并删除了客户端,而当前检查正在等待。 / p>
match()
不能两次匹配同一客户端,这很重要(原子部分)。match()
仍然可以匹配同时删除的客户端(通过经典的地图API删除,而不是通过相同的功能删除),并且这是可以容忍的。
public void idSelected(int id,int selectedId){
Client playerA=clients.get(id);
if(playerA!=null){
synchronized(playerA){
playerA.selectedId=selectedId;
}
}
}
public Client[] match(int id,int selectedId){
// determine if players exist in order be synchronized onto
Client playerA=clients.get(id);
if(playerA==null){
return null;
}
Client playerB=clients.get(selectedId);
if(playerB==null){
return null;
}
// sort players in order to do nested synchronization safely
if(id>selectedId){
final Client t=playerA;
playerA=playerB;
playerB=t;
}
// check under synchronization
synchronized(playerA){
if(clients.containsKey(playerA.id)){
synchronized(playerB){
if(clients.containsKey(playerB.id)){
if(playerA.selectedId==playerB.id&&playerB.selectedId==playerA.id){
clients.remove(id);
clients.remove(selectedId);
return new Client[]{playerA,playerB};
}
}
}
}
}
return null;
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)