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;
}
(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.