ConcurrentHashMap上的原子多条目操作 编辑1 编辑2

问题描述

我需要在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 (将#修改为@)