我想知道是否需要同步或使用并发类,或者相反,如果对地图的唯一修改正在改变,那么在多线程环境中使用非并发类并且在地图上不进行同步是相对的线程安全地图的价值。
我问这个的原因是HashMap(以及其他非并发地图文档)有这样的评论:
请注意,此实现未同步。 如果多个线程同时访问哈希映射,并且至少有一个 线程在结构上修改地图,必须 外部同步。 (结构修改是任何操作 添加或删除一个或多个映射;只是改变价值 与实例已包含的键不相关联的键 结构修改。)这通常由 同步一些自然封装地图的对象。
这让我相信如果修改不是结构性的(即没有添加或删除)我应该能够更新(非并发)地图同步。
我读这是对的吗?即将地图中的值更新为原子过程吗?
答案 0 :(得分:5)
更新地图值不是原子过程。但是,每个尝试同时修改映射值的多个不同线程不会因并发错误而导致非常奇怪的异常或错误。例如,您不会导致其中一个键/值对消失,或者从地图中删除随机元素。
但是,一个线程在更新键/值对时所做的更新不一定对其他线程可见,除非有其他同步正在进行(例如,如果值是AtomicIntegers
之类的话) 。除此之外,无法保证线程甚至会看到自己的更新,因为它们可能会受到其他线程的破坏。
希望这有帮助!
答案 1 :(得分:2)
将某些内容放在HashMap
中不是原子操作:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
将HashMap
与Collections#synchronizedMap
包裹在一起可能是值得的。
答案 2 :(得分:2)
HashMap
,TreeMap
等地图的实现在更新时不是原子的,也不是线程安全的,但是当你使用{{3}时,可以实现原子更新操作自Java 1.8以来。
例如,以下方法将添加特定键的值,或者如果该键没有以前的值,则设置该值。
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
int addValue(String key, int value) {
return map.compute(key, (k, v) -> v == null ? value : v + value);
}