2

我有一个ConcurrentHashMap订阅包含另一个对象(sessionCollection),我需要做下面的迭代操作:如何使原子在ConcurrentHashMaps上进行嵌套迭代操作?

subscriptions.values().forEach(sessionCollection -> 
    sessionCollection.removeAllSubscriptionsOfSession(sessionId)); 

其中sessionCollection.removeAllSubscriptionsOfSession确实在内部集合(也ConcurrentHashMap)重复另一操作sessionCollection

// inside SessionCollection: 
private final ConcurrentHashMap<String, CopyOnWriteArrayList<String>> topicsToSessions = 
new ConcurrentHashMap<>(); 

public void removeAllSubscriptionsOfSession(String sessionId) { 
    // Remove sessions from all topics on record 
    topicsToSessions.keySet().forEach(topicSessionKey -> 
    removeTopicFromSession(sessionId, topicSessionKey)); 
} 

什么是使整体在组合操作?

回答

1

ConcurrentHashMap具有批量操作(forEach*()),但它们相对于整个地图不是原子的。在地图上进行原子批处理更改的唯一方法是自己实现所有必需的同步。例如,通过明确使用​​块或通过为地图创建包装器(或扩展)来在需要时处理同步。在这种情况下,一个简单的HashMap就足够了,因为你要做的同步反正:

public class SubscriptionsRegistry { 
    private final Map<Integer, SessionCollection> map = new HashMap<>(); 

    public synchronized void removeSubscriptions(Integer sessionId) { 
     map.values().forEach(...); 
    } 

    public synchronized void addSubscription(...) { 
     ... 
    } 

    ... 
} 

您也想保护的主题对会议的地图(至少他们的修改版本)从您的SubscriptionsRegistry外泄漏,所以没有适当的同步,没有人能够修改它们。

+0

您还可以使用Collections.synchronizedMap并在地图上进行同步,因为synchronizedMap支持客户端锁定。虽然性能会比ConcurrentHashMap差很多。您需要决定是否确保整个地图被锁定对于您的逻辑来说确实非常重要,如果是这样的话,除了锁定整个地图之外别无选择。在很多情况下,这种锁定是不必要的。 –

+0

我同意,'SynchronizedMap.forEach()'可以用来执行同步的批量更新。从技术上讲,它与我描述的是相同的包装,但没有操作语义。 –