Java ConcurrentHashMap的小测试(2)

1 ConcurrentHashMap读取数据不加锁的结论是不正确的,当读取的值为null时,这时候ConcurrentHashMap是会加锁再次读取该值的(上面粗体部分)。至于读到null就加锁再读的原因如下:

  ConcurrentHashMap的put方法value是不能为null的(稍后代码展示),现在get值为null,那么可能有另外一个线程正在改变该值(比如remove),为了读取到正确的值,所以采取加锁再读的方法。在此对Doug Lee大师的逻辑严密性佩服得五体投地啊有木有......

2 读者大概也知道为啥不是20了吧,虽然put加锁控制了线程的执行顺序,但是get没有锁,也就是多个线程可能拿到相同的值,然后相同的值+1,结果就不是预期的20了。

既然知道了原因,那么修改一下add(String key)这个方法,加锁控制它get的顺序即可。

public void add(String key) { lock.lock(); try { Integer value = map.get(key); System.out.println("当前数量" + value); if (null == value) { map.put(key, 1); } else { map.put(key, value + 1); } } finally { lock.unlock(); } }

再次debug输出一下:

当前数量null 当前数量1 当前数量2 当前数量3 当前数量4 当前数量5 当前数量6 当前数量7 当前数量8 当前数量9 当前数量10 当前数量11 当前数量12 当前数量13 当前数量14 当前数量15 当前数量16 当前数量17 当前数量18 当前数量19 统计的数量:20

得到正确的结果。

附上put方法的源码:

public V put(K key, V value) { if (value == null) throw new NullPointerException();// 值为空抛出NullPointerException异常 int hash = hash(key.hashCode());// 根据key的hashcode 然后获取hash值 return segmentFor(hash).put(key, hash, value, false); //定位到某个segment } V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { int c = count; if (c++ > threshold) // 该segment总的key-value数量+ 大于threshold阀值 rehash(); // segment扩容 HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1);// hash值与数组长度-1取&运算 HashEntry<K,V> first = tab[index]; // 定位到某个数组元素(头节点) HashEntry<K,V> e = first;// 头节点 while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; V oldValue; if (e != null) {// 找到key 替换旧值 oldValue = e.value; if (!onlyIfAbsent) e.value = value; }else {// 未找到key 生成节点 oldValue = null; ++modCount; tab[index] = new HashEntry<K,V>(key, hash, first, value); count = c; // write-volatile } return oldValue; } finally { unlock(); } }

程序员最怕对技术似懂非懂,在此与君共勉,慎之戒之!!!

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c70bdc810d0f97b8073468333dac084b.html