![](/img/trans.png)
[英]Does a ConcurrentHashMap need to be wrapped in a synchronized block?
[英]Does get block ConcurrentHashMap?
片段 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);
}
此代码段在 UI 线程以外的线程上运行。 每次执行时,ListView 滚动总是变慢,所以代码段中一定有什么东西阻塞了 UI 线程。
我发现NameHolder nameHolder = mNameCache.get(callId);
将阻塞mNameCache
直到mNameCache.put(callId, nameHolder);
. 但是文档说“ConcurrentHashMap”不会阻止检索操作。 我不知道出了什么问题。
AFAIK,它可以阻止。
(如果我在ConcurrentHashMap中的理解错误,请纠正我)。
ConcurrentHashMap的整个想法是,有一个大数组存储哈希表,每个人都锁定整个表,它被分成几个分区(你可以在ConcurrentHashMap的源代码中看到内部类“Segment”)。 只有当您正在读取或写入不同的分区时,存在“无争用”的情况。
看看在另一个答案中引用的源代码Stephen C,你可以在readValueUnderLock()
看到lock()
和unlock()
readValueUnderLock()
。 如果两个线程正在访问同一个分区,它将锁定该段并完成其工作。
因此,如果你的UI线程被put
相同的键(或同一段中的其他键),它将阻塞,直到你完成你的get()
但是,在您提出的问题中,它并没有阻止。 它仅在访问(get / put等)期间阻塞,并且一旦操作完成就释放锁。
简单回答是不。 除非您没有告诉我们其他内容,否则get
呼叫将不会阻止超过一微秒左右。
get
方法的源代码及其辅助方法如下所示。 正如您所看到的,大部分工作都是在没有任何锁定的情况下完成的。 最终获取条目值是在锁定下完成的,但锁定将几乎立即释放...在finally
块中。
可以肯定地说, get()
调用不是导致问题的原因。
/**
* 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;
}
资料来源: http : //www.java2s.com/Open-Source/Android/android-core/platform-libcore/java/util/concurrent/ConcurrentHashMap.java.htm
(如果链接中断,Google“ConcurrentHashMap android source”。)
我检查了代码 java ConcurrentHashMap JDK 17。get() 不锁定 Bucket。 这意味着您可以在存储桶写入(放入或删除)期间读取存储桶。
但是存储桶在写入期间被锁定。 这意味着只有一个线程可以同时写入更改存储桶。
所以,并发读/写时有以下三种情况:
案例 1:先读后写。 所以读取访问旧存储桶
case 2 : read after write 包括2种情况:
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
注意读比写快。 所以案例 2.1可能会发生
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.