最近学习了一下CouncurrentHashMap的源码 突然有个疑问
rehash
void rehash() {
            HashEntry<K,V>[] oldTable = table;
            int oldCapacity = oldTable.length;
            if (oldCapacity >= MAXIMUM_CAPACITY)
                return;            /*
             * Reclassify nodes in each list to new Map.  Because we are
             * using power-of-two expansion, the elements from each bin
             * must either stay at same index, or move with a power of two
             * offset. We eliminate unnecessary node creation by catching
             * cases where old nodes can be reused because their next
             * fields won't change. Statistically, at the default
             * threshold, only about one-sixth of them need cloning when
             * a table doubles. The nodes they replace will be garbage
             * collectable as soon as they are no longer referenced by any
             * reader thread that may be in the midst of traversing table
             * right now.
             */            HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1);
            threshold = (int)(newTable.length * loadFactor);
            int sizeMask = newTable.length - 1;
            for (int i = 0; i < oldCapacity ; i++) {
                // We need to guarantee that any existing reads of old Map can
                //  proceed. So we cannot yet null out each bin.
                HashEntry<K,V> e = oldTable[i];                if (e != null) {
                    HashEntry<K,V> next = e.next;
                    int idx = e.hash & sizeMask;                    //  Single node on list
                    if (next == null)
                        newTable[idx] = e;                    else {
                        // Reuse trailing consecutive sequence at same slot
                        HashEntry<K,V> lastRun = e;
                        int lastIdx = idx;
                        for (HashEntry<K,V> last = next;
                             last != null;
                             last = last.next) {
                            int k = last.hash & sizeMask;
                            if (k != lastIdx) {
                                lastIdx = k;
                                lastRun = last;
                            }
                        }
                        newTable[lastIdx] = lastRun;                        // Clone all remaining nodes
                        for (HashEntry<K,V> p = e; p != lastRun; p = p.next) {
                            int k = p.hash & sizeMask;
                            HashEntry<K,V> n = newTable[k];
                            newTable[k] = new HashEntry<K,V>(p.key, p.hash,
                                                             n, p.value);
                        }
                    }
                }
            }
            table = newTable;
        }我发现在代码执行开始
 HashEntry<K,V>[] oldTable = table; 获取了最新的table(table是volatile的)
代码执行过程并未加锁,那么在高并发环境下,在rehash时势必会丢失一些值,不知道我理解的对不对,哪位高手能详细的解释下。
ConcurrentHashMapCouncurrentHashMap问题

解决方案 »

  1.   

    之前也看过这个类的,除了那个volatile关键字外,LZ可以看看它的put或remove操作的实现,往里跟,在内部类Segment<K, V>有个lock();操作的,这就解决了高并发的问题了。
      

  2.   

    rehash怎么会不加锁呢?增删改操作都是加锁了的。
    首先会尝试tryLock,如果失败,再调用scanAndLock,返回的时候已经加锁了。
      

  3.   

    put 和 remove确实有锁,实现我也看了,这里说的rehash确实没有加锁。volatile只能保证
     HashEntry<K,V>[] oldTable = table;  这句时得到的table是最新的。但是后续程序执行都是通过中间变量oldTable去做的。rehash确实没看到锁。
      

  4.   


    你可以现在看下源码。 rehash确实没有锁
      

  5.   

    抱歉,从put中进行rehash()时候确实加了锁。没看到结帖子了!
      

  6.   

    put 和 remove确实有锁,实现我也看了,这里说的rehash确实没有加锁。volatile只能保证
     HashEntry<K,V>[] oldTable = table;  这句时得到的table是最新的。但是后续程序执行都是通过中间变量oldTable去做的。rehash确实没看到锁。这是我看到的源码:
            V put(K key, int hash, V value, boolean onlyIfAbsent) {
                lock();
                try {
                    int c = count;
                    if (c++ > threshold) // ensure capacity
                        rehash();
                        ...
    lock操作是在最前面的,这个应该能满足了吧?