Java 并发哈希映射同时插入

Java 并发哈希映射同时插入,java,multithreading,concurrenthashmap,Java,Multithreading,Concurrenthashmap,我已经读到,在Java的并发hashmap中,同步插入是可能的,因为它被划分为多个段,每个段都有单独的锁。 但如果两个插入发生在同一个片段上,那么这些同时发生的插入就不会发生。 我的问题是在这种情况下会发生什么?第二次插入会等到第一次插入完成吗 看一看。它描述了可用于处理并发map突变的额外方法。请查看。它描述了可用于处理并发map突变的额外方法。如果两个更新尝试在同一段上发生,它们将相互竞争,其中一个将不得不等待。您可以通过选择一个ConcurrentyLevel值来优化它,该值考虑将同时更新

我已经读到,在Java的并发hashmap中,同步插入是可能的,因为它被划分为多个段,每个段都有单独的锁。 但如果两个插入发生在同一个片段上,那么这些同时发生的插入就不会发生。
我的问题是在这种情况下会发生什么?第二次插入会等到第一次插入完成吗

看一看。它描述了可用于处理并发map突变的额外方法。

请查看。它描述了可用于处理并发map突变的额外方法。

如果两个更新尝试在同一段上发生,它们将相互竞争,其中一个将不得不等待。您可以通过选择一个ConcurrentyLevel值来优化它,该值考虑将同时更新hashmap的线程数


您可以在

中找到所有详细信息,如果两个更新尝试在同一段上发生,它们将相互竞争,其中一个将不得不等待。您可以通过选择一个ConcurrentyLevel值来优化它,该值考虑将同时更新hashmap的线程数


您可以在

中找到所有详细信息。一般来说,您不必太关心如何实现
ConcurrentHashMap
。它只是遵守合同的规定,而合同的规定确保同时进行修改是可能的

但要回答您的问题:是的,一个插入可能会等待另一个插入的完成。在内部,它使用锁来确保一个线程等待另一个线程释放锁。内部使用的类
Segment
实际上继承自
ReentrantLock
。以下是Segmenet.put()的缩写版本:

final V put(K键,int散列,V值,仅布尔值){
HashEntry node=tryLock()?null:scanAndLockForPut(键、哈希、值);
V旧值;
试一试{
//修改
}最后{
解锁();
}
返回旧值;
}
私有HashEntry scanAndLockForPut(K键,int散列,V值){
// ...
int retries=-1;//查找节点时为负数
而(!tryLock()){
如果(重试次数<0){
// ...
}
否则,如果(++重试次数>最大扫描次数){
锁();
打破
}
else if((重试次数&1)==0&&(f=entryForHash(this,hash))!=first){
e=first=f;//如果条目更改,则重新遍历
重试次数=-1;
}
}
返回节点;
}

这可能会给你一个想法。

一般来说,你不需要太在意
ConcurrentHashMap
是如何实现的。它只是遵守合同的规定,而合同的规定确保同时进行修改是可能的

但要回答您的问题:是的,一个插入可能会等待另一个插入的完成。在内部,它使用锁来确保一个线程等待另一个线程释放锁。内部使用的类
Segment
实际上继承自
ReentrantLock
。以下是Segmenet.put()的缩写版本:

final V put(K键,int散列,V值,仅布尔值){
HashEntry node=tryLock()?null:scanAndLockForPut(键、哈希、值);
V旧值;
试一试{
//修改
}最后{
解锁();
}
返回旧值;
}
私有HashEntry scanAndLockForPut(K键,int散列,V值){
// ...
int retries=-1;//查找节点时为负数
而(!tryLock()){
如果(重试次数<0){
// ...
}
否则,如果(++重试次数>最大扫描次数){
锁();
打破
}
else if((重试次数&1)==0&&(f=entryForHash(this,hash))!=first){
e=first=f;//如果条目更改,则重新遍历
重试次数=-1;
}
}
返回节点;
}
这会给你一个主意。

大多数并发数据结构的启发是,有一个先修改的后台数据结构,有一个对外部方法可见的面向前端的数据结构。然后,当修改完成时,将备份数据结构设置为公共数据结构,并将公共数据结构推到后面。事情远不止这些,但这是典型的合同。


大多数并发数据结构的启发是,有一个先修改的后台数据结构,有一个对外部方法可见的面向前端的数据结构。然后,当修改完成时,将备份数据结构设置为公共数据结构,并将公共数据结构推到后面。还有更多的方法,但这是典型的契约。

ConcurrentHashMap包含段数组,而段数组又包含HashEntry数组。每个HashEntry都保存一个键、一个值和一个指向下一个相邻条目的指针


但它在段级别获得了锁定。因此你是对的。i、 第二次插入将等待第一次插入完成

ConcurrentHashMap包含段数组,而段数组又包含HashEntry数组。每个HashEntry都保存一个键、一个值和一个指向下一个相邻条目的指针


但它在段级别获得了锁定。因此你是对的。i、 第二次插入等待第一次插入完成

您是否阅读了
ConcurrentHashMap的Javadocs
?q1)它仍然有效,q2)是的,第二次插入必须等待。如果您希望减少这种情况发生的几率,您可以增加段数,但您很少需要这样做。@Kayaman-阅读javadocs,但没有发现与我的问题相关的任何内容。@Peter-谢谢Peter的回答。您是否阅读了
ConcurrentHashMap
?q1)的javadocs仍然有效,q2)是的,第二个必须等待。你可以增加这个数字
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
    HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);
    V oldValue;
    try {
       // modifications
    } finally {
        unlock();
    }
    return oldValue;
}

private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
    // ...
    int retries = -1; // negative while locating node
    while (!tryLock()) {
        if (retries < 0) {
            // ...
        }
        else if (++retries > MAX_SCAN_RETRIES) {
            lock();
            break;
        }
        else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first) {
            e = first = f; // re-traverse if entry changed
            retries = -1;
        }
    }
    return node;
}