首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

java ConcurrentHashMap中的一点点不解

2012-11-06 
java ConcurrentHashMap中的一点点迷惑顺便再温习一下ConcurrentHashMap源码,读到isEmpty()方法时,对其中

java ConcurrentHashMap中的一点点迷惑

顺便再温习一下ConcurrentHashMap源码,读到isEmpty()方法时,对其中的:

?

public boolean isEmpty() {        final Segment<K,V>[] segments = this.segments;        /*         * We keep track of per-segment modCounts to avoid ABA         * problems in which an element in one segment was added and         * in another removed during traversal, in which case the         * table was never actually empty at any point. Note the         * similar use of modCounts in the size() and containsValue()         * methods, which are the only other methods also susceptible         * to ABA problems.         */        int[] mc = new int[segments.length];        int mcsum = 0;        for (int i = 0; i < segments.length; ++i) {            if (segments[i].count != 0)                return false;            else                mcsum += mc[i] = segments[i].modCount;        }        // If mcsum happens to be zero, then we know we got a snapshot        // before any modifications at all were made.  This is        // probably common enough to bother tracking.        if (mcsum != 0) {            for (int i = 0; i < segments.length; ++i) {                if (segments[i].count != 0 ||                    mc[i] != segments[i].modCount)                    return false;            }        }        return true;    }

?

? ?产生了一些问题(其实叫疑问更好,有可能不是问题):

? ?1、从Segment内部类中可以看到modCount属性是非volatile的,也就是如果对其进行了修改,在isEmpty()方法中对其值的访问不一定能访问到最新值。

? ?2、使用mcsum值并不能完全解决ABA问题情况,因为mcsum += mc[i] = segments[i].modCount;这句话只对for循环中还没有循环到的变量可能会读取到最新值,但是对已经循环过的segment,如果其间有值加入,这时的isEmpty()语意是无法保证的,因为mcsum值已经累加过了以前的值,并无法感知最新值。

例如:此时for循环刚好循环到segments[3],此时已经累加了前面segments[0],segments[1],segments[2]的modCount,而此时刚好segments[0]被插入了一个新值,此时segments[0].modCount值是变化了的,而此时已经无法再修改mcsum的值了。因为这些变化都是发生在该遍历期间的,理应算做在计算期间的结果,如果该计算结果为空,而在计算的期间是有值插入的,则该计算期间的非空应该作为返回结果:非空。。

? 3、再次确认非空时:

? ? ? ?if (mcsum != 0) {

            for (int i = 0; i < segments.length; ++i) {                if (segments[i].count != 0 ||                    mc[i] != segments[i].modCount)                    return false;            }        }

? ?这个逻辑再次无法保证,因为没有使用同步,再次无法保证读取到的modCount是最新值(据自己了解的并发知识,modCount有可能会被线程缓存,而无法得到最新值)。

?

? ?带着这众多的疑问,我找遍了网络都没有找到合理的说法。。。。

?

? ?最后在一本书《Java并发编程实践》中找到了一个比较合理的说法

?

?

ConcurrentHashMap尽管有这么多改进,我们仍然有一些需要权衡的地方。那些对整个Map进行操作的方法,如size和isEmpty,它们的语义在反映容器并发特性上已经被轻微的弱化了。因为size的结果相对于在计算的时候可能已经过期,它仅仅是一个估算值,所以允许size返回一个近似值而不是一个精确值。这在一开始让人有些困扰,不过事实上像size和isEmpty这样的方法在并发环境下几乎没有什么用处,因为它们的目标是运动的。所以对这些操作的需求被弱化了。相反,应该保证对最重要的操作进行性能优化,首先就包括get、put,containsKey和remove。

?

?

? 茅塞顿开,这就是为什么可以对isEmpty可以采取非加锁方式了,同时如果某个方法在调用isEmpty时进行对Segment加锁仍能达到putIfAbsent的效果!

?

?

热点排行