为什么读方法,不需要同步?
因为数据有内存可见性,即加了volatile关键字。有内存可见性之后,只要有线程更新数据,那么其他线程就可以看到最新数据。因为数据被更新:从线程内存——》共享内存。
即1.线程内存2.共享内存。这是本质。//具体来说,就是1.以前线程更新数据,只在当前线程内存可见最新数据。其他线程可能是旧的数据,也可能是新的数据。2.现在,加了可见性关键字,就一定会把新数据从线程内存更新到共享内存,所以其他线程一定可以读最新数据。
代码
/* ---------------- Fields -------------- */
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
*/
transient volatile Node<K, V>[] table; //有内存可见性。
参考
mp.weixin.qq.com/s/LAWSThrgP…
为什么jdk8 有了cas,还要synchronized?
jdk8的写方法,有cas?干嘛还要同步关键字?
因为要处理两种不同的情况:
1.数据不存在,添加数据 //基于cas,交换null(数组里当前key——》index的value是null)和新数据
2.数据存在,覆盖旧数据的值 /赋值语句,添加同步关键字
代码
/**
* Implementation for put and putIfAbsent
*
* 写数据的时候,有三种情况
* 1.第一次,数组为null,所以要初始化数组,然后再写数据(for循环)
* 2.数据不存在,写数据
* 3.数据存在,覆盖旧数据的值
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) { //key和value都不能为null,否则报错-空指针异常
throw new NullPointerException();
}
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K, V>[] tab = table; ; ) {
Node<K, V> f; //节点数据
int n, i, fh;
if (tab == null || (n = tab.length) == 0) { //1.数组为空
tab = initTable(); //初始化数组,然后for循环,数据不存在
} else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//2.数据存在 //添加新的数据,基于cas
if (casTabAt(tab, i, null,
new Node<K, V>(hash, key, value, null))) {
break; // no lock when adding to empty bin
}
} else if ((fh = f.hash) == MOVED) {
tab = helpTransfer(tab, f);
} else { //3.数据不存在 //覆盖旧数据的值,使用同步关键字
V oldVal = null;
synchronized (f) { //jdk8-没有使用显式锁,而是直接使用的synchronzied关键字,说明jdk8对synchronized进行了大量的优化
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K, V> e = f; ; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent) {
e.val = value;
}
break;
}
Node<K, V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K, V>(hash, key,
value, null);
break;
}
}
} else if (f instanceof TreeBin) {
Node<K, V> p;
binCount = 2;
if ((p = ((TreeBin<K, V>) f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent) {
p.val = value;
}
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD) {
treeifyBin(tab, i); //如果链表数据太多,那么链表转换为红黑树
}
if (oldVal != null) {
return oldVal;
}
break;
}
}
}
addCount(1L, binCount);
return null;
}
参考
jdk8源码