gpt4 book ai didi

Java Collection-Within-Collection 并发

转载 作者:行者123 更新时间:2023-11-30 11:41:50 24 4
gpt4 key购买 nike

我正在尝试创建一个使用映射内集合线程安全的类。我不确定 what特别需要同步。

map 的定义类似于Map<Class<K>, Set<V>> map;。 .以下是在实现中内部使用 map 的方式的缩减:

public void addObject(K key, V object) {
getSet(key).add(object);
}

public void removeObject(K key, V object) {
getSet(key).remove(object);
}

public void iterateObjectsInternally(K key, Object... params)
{
for (V o : getSet(key)) {
o.doSomething(params);
}
}

private Set<V> getSet(K key) {
if (!map.containsKey(key)) {
map.put(key, new Set<V>());
}

return map.get(key);
}

map 问题

至于使用map本身,我看到的唯一并发问题是在 getSet(K) 中,其中线程上下文可以在 containsKey 之间切换和 put .在这种情况下,可能会发生以下情况:

[Thread A] map.containsKey(key)       => returns false
[Thread B] map.containsKey(key) => returns false
[Thread B] map.put(key, new Set<V>())
[Thread B] map.get(key).add(object)
[Thread A] map.put(key, new Set<V>()) => Thread A ovewrites Thread B's object [!]
[Thread B] map.get(key).add(object)

现在,我正在使用常规 HashMap对于这个实现。而且,如果我是正确的,使用 Collection.synchronizedMap()ConcurrentHashMap只会解决方法级别的并发问题。也就是说,方法将以原子方式执行。这些并没有说明方法相互交互的方式,因此即使使用并发解决方案,以下情况仍然可能发生。

ConcurrentHashMap但是,确实有方法 putIfAbsent .这样做的缺点是声明 map.putIfAbsent(key, new Set<V>())每次请求集合时都会创建一个新集合。这似乎有很多开销。

另一方面,将这两个语句简单地包装在一个同步块(synchronized block)中就足够了吗?

synchronized(map) {
if (!map.containsKey(key)) {
map.put(key, new Set<V>());
}
}

有没有比锁定整个 map 更好的方法?有没有办法只锁定键,这样就不会锁定对 map 其他值的读取?

synchronized(key) {
if (!map.containsKey(key)) {
map.put(key, new Set<V>());
}
}

请记住,键不一定是相同的对象(它们具体是 Class<?> 类型),但哈希码相等。通过 key 同步如果同步需要对象地址相等,则可能无法工作。

设置问题

我认为,更大的问题是了解这套装置是否被正确使用。有几个问题:添加对象、删除对象和迭代对象。

将列表包装在 Collections.synchronizedList 中足以避免 addObject 中的并发问题和 removeObject ?我假设这会很好,因为同步包装器会使它们成为原子操作。

但是,迭代可能是另一回事。对于 iterateObjectsInternally ,即使集合是同步的,它仍然必须在外部同步:

Set<V> set = getSet(key);
synchronized(set) {
for (V value : set) {
// thread-safe iteration
}
}

然而,这似乎是一种可怕的浪费。相反,如果我们简单地使用 CopyOnWriteArrayList 替换怎么办?或 CopyOnWriteArraySet作为定义。由于迭代将简单地使用数组内容的快照,因此无法从另一个线程修改它。另外,CopyOnWriteArrayList在 add 和 remove 方法上使用可重入锁,这意味着 add/remove 本质上也是安全的(因为它们是同步方法)。 CopyOnWriteArrayList看起来很有吸引力,因为内部结构的迭代次数远远超过列表中的修改次数。此外,使用复制的迭代器,无需担心 addObjectremoveObject搞乱了 iterateObjectInternally 的迭代( ConcurrentModificationExceptions ) 在另一个线程中。

这些并发检查是否在正确的轨道上和/或是否足够严格?我是一个有并发编程问题的新手,我可能遗漏了一些明显的东西,或者想得太多了。我知道有几个类似的问题,但我的实现方式似乎有所不同,足以保证像我一样具体地提出问题。

最佳答案

你肯定想多了。根据您的并发特征使用简单的 ConcurrentHashMap 和 ConcurrentSkipListSet/CopyOnWriteArraySet(主要是如果迭代需要考虑数据的即时修改)。使用类似于以下代码片段的方法作为 getSet 方法:

private Set<V> getSet(K key) {
Set<V> rv = map.get(key);
if (rv != null) {
return rv;
}
map.putIfAbsent(key, new Set<V>());
return map.get(key);
}

这将确保在添加/删除对象时正确的无锁并发,对于迭代,您将需要确定缺少更新是否是您的问题域中的一个问题。如果在迭代期间添加新对象时遗漏新对象不是问题,请使用 CopyOnWriteArraySet。

另一方面,您想深入了解可以使用 w.r.t. 的粒度类型。并发性,您的要求是什么,边缘情况下的正确行为是什么,最重要的是,您的代码必须涵盖哪些性能和并发性特征 - 如果它在启动时发生两次,我只是让所有方法同步并成为完成它。

关于Java Collection-Within-Collection 并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12101619/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com