我注意到,ConcurrentHashMap已经在Java 8中被完全重写为更“无锁”。我浏览了get()方法的代码,发现没有显式的锁机制:
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}问题:
如何从一个线程中查看如何从其他线程对此hashmap进行修改,因为代码不是在同步伞下(这将强制执行happens-before 关系)?。
注意:整个ConcurrentHashMap是一个表的包装器:transient volatile Node<K,V>[] table;
所以table是对数组的易失性引用,而不是对易失性元素数组的引用!这意味着,如果有人在此数组中更新元素,则修改将不会出现在其他线程中。
发布于 2017-06-09 14:47:11
简短回答
Node#val是volatile,它确定您在订货前发生的情况。
较长答案
synchronized不是线程安全的要求,它是工具箱中使系统线程安全的一个工具。为了考虑线程安全,您必须考虑这个ConcurrentHashMap上的一整套操作。
知道原始的ConcurrentHashMap也是非阻塞的是有用的。注意之前的Java 8 CHM get
V get(Object key, int hash) {
if (count != 0) { // read-volatile
HashEntry<K,V> e = getFirst(hash);
while (e != null) {
if (e.hash == hash && key.equals(e.key)) {
V v = e.value;
if (v != null)
return v;
return readValueUnderLock(e); // ignore this
}
e = e.next;
}
}
return null;
}在这种情况下,没有阻塞,那么它是如何工作的?HashEntry#value是volatile。这是线程安全的同步点。
CHM-8的Node类是相同的.
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;因此,在这种情况下,一个非空的val应该确保你发生-之前的关系有关的行动之前,一个空头。
发布于 2017-06-09 14:44:34
文档没有声明同步发生。例如,它声明
..。聚合操作(如
putAll和clear)、并发检索可能只反映某些条目的插入或删除。
换句话说,允许并发使用和提供同步访问是有区别的。
发布于 2017-06-11 17:30:18
Java语言规范写入
如果我们有两个动作x和y,我们写hb(x,y)来表示x发生在y之前。
和定义
同步操作诱导同步-与动作的关系,定义如下:
也就是说,读取易失性字段就会发生--就像显式锁一样。
https://stackoverflow.com/questions/44460503
复制相似问题