简体   繁体   中英

Does get block ConcurrentHashMap?

Snippet 1:

private void startLoadingName() {
    for (ConcurrentHashMap.Entry<TextView, Long> entry : mPendingNameRequest.entrySet()) {
        long callId = (Long)entry.getValue();
        NameHolder nameHolder = mNameCache.get(callId);
        nameHolder.name = QueryUtils.loadNameFromDb(mContext, callId);
        nameHolder.status = NameHolder.LOADED;
        // mNameCache is a ConcurrentHashMap
        mNameCache.put(callId, nameHolder);

        updateContactCachedName(callId, nameHolder);
    }

    GsItemLoader.this.sendEmptyMessage(MESSAGE_SET_NAME);
}

This snippet is run on a thread other than the UI thread. Every time it executes, the ListView scroll always slows down, so there must be something in the snippet that blocks the UI thread.

I found that NameHolder nameHolder = mNameCache.get(callId); will block mNameCache until mNameCache.put(callId, nameHolder); . But the docs says 'ConcurrentHashMap' won't block in retrieval operations. I can't figure out what's going wrong.

AFAIK, it MAY block.

(Please correct me if my understanding in ConcurrentHashMap is wrong).

The whole idea of ConcurrentHashMap is, instead having one big array storing the hash table and everyone is locking the whole table, it is in splitted into partitions (you can see the inner class "Segment" in ConcurrentHashMap's source code). The case that there is "no contention" is only when you are reading or writing to different partitions.

Look close to the source code Stephen C quoted in the other answer, you can see lock() and unlock() in readValueUnderLock() . If two thread is accessing same partition, it will lock the segment and do its work.

Therefore, if your UI thread is put ting to the same key (or other key in same segment), it will block until you finish your get()

However, it is not blocking in the sense you are talking about in your question. It only blocks for the period of accessing (get/put etc), and lock is released once the operation is finished.

The simple answer is no. Unless there is something else you are not telling us, the get call won't block for longer than a microsecond or so.

The source code for the get method and its helper method are below. As you can see, most of the work is done without any locks whatsoever. The final fetch of the entry value is done under a lock, but the lock will be released almost instantly ... in a finally block.

It is safe to say that the get() call is not the cause of your problem.


    /**
     * Reads value field of an entry under lock. Called if value
     * field ever appears to be null. This is possible only if a
     * compiler happens to reorder a HashEntry initialization with
     * its table assignment, which is legal under memory model
     * but is not known to ever occur.
     */
    V readValueUnderLock(HashEntry<K,V> e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }

    /* Specialized implementations of map methods */

    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); // recheck
                }
                e = e.next;
            }
        }
        return null;
    }

Source: http://www.java2s.com/Open-Source/Android/android-core/platform-libcore/java/util/concurrent/ConcurrentHashMap.java.htm

(If the link breaks, Google "ConcurrentHashMap android source".)

I checked code java ConcurrentHashMap JDK 17. get() doesn't lock Bucket. it means that you can read Bucket during The bucket is writing (put or remove).

But bucket is locked during write. It means that only one thread can write to change bucket at the same time.

So, there are three case when read/write concurrently:

case 1: read before write. so read access old bucket

case 2 : read after write include 2 cases:

   case 2.1: read access old bucket if write has not finished changing during read access

   case 2.2: read access new bucket if write has finished changing during read access

notice that read is more faster than write. so case 2.1 can happen

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM