Java 并发hashMap putIfAbsent方法功能

Java 并发hashMap putIfAbsent方法功能,java,concurrenthashmap,Java,Concurrenthashmap,我是java世界的新手,探索并发哈希映射,在探索并发哈希映射API的同时,我发现了putifAbsent()方法 现在请告诉我们什么是it功能,我们什么时候实际需要它,如果可能,请用一个简单的小例子解释。aConcurrentHashMap的设计是为了让大量并发线程使用它 现在,如果您使用标准Map接口提供的方法,您可能会编写类似的代码 if(!map.containsKey("something")) { map.put("something", "a value"); }

我是java世界的新手,探索并发哈希映射,在探索并发哈希映射API的同时,我发现了putifAbsent()方法


现在请告诉我们什么是it功能,我们什么时候实际需要它,如果可能,请用一个简单的小例子解释。

a
ConcurrentHashMap
的设计是为了让大量并发
线程使用它

现在,如果您使用标准
Map
接口提供的方法,您可能会编写类似的代码

  if(!map.containsKey("something")) {
      map.put("something", "a value");
  }
这看起来不错,似乎可以完成任务,但它不是线程安全的。然后你会想,“啊,但是我知道
synchronized
关键字”,然后把它改成这个

  synchronized(map) {
      if(!map.containsKey("something")) {
          map.put("something", "a value");
      }
  }
这解决了问题

现在,您所做的是在检查密钥是否存在并将其添加到映射中时,锁定整个映射以进行读写操作

这是一个非常粗糙的解决方案。现在,您可以通过双重检查锁和重新锁定密钥等方式实现自己的解决方案,但这是一个非常复杂的代码,非常容易出现bug

因此,您可以使用JDK提供的解决方案

ConcurrentHashMap
是一个聪明的实现,它将
Map
划分为多个区域并单独锁定,这样您就可以在没有外部锁定的情况下对映射进行并发、线程安全的读写


与实现中的所有其他方法一样,
putIfAbsent
锁定密钥的区域,而不是整个
Map
,因此允许同时在其他区域进行其他操作。

A
ConcurrentHashMap
的设计使得大量并发
线程可以使用它

现在,如果您使用标准
Map
接口提供的方法,您可能会编写类似的代码

  if(!map.containsKey("something")) {
      map.put("something", "a value");
  }
这看起来不错,似乎可以完成任务,但它不是线程安全的。然后你会想,“啊,但是我知道
synchronized
关键字”,然后把它改成这个

  synchronized(map) {
      if(!map.containsKey("something")) {
          map.put("something", "a value");
      }
  }
这解决了问题

现在,您所做的是在检查密钥是否存在并将其添加到映射中时,锁定整个映射以进行读写操作

这是一个非常粗糙的解决方案。现在,您可以通过双重检查锁和重新锁定密钥等方式实现自己的解决方案,但这是一个非常复杂的代码,非常容易出现bug

因此,您可以使用JDK提供的解决方案

ConcurrentHashMap
是一个聪明的实现,它将
Map
划分为多个区域并单独锁定,这样您就可以在没有外部锁定的情况下对映射进行并发、线程安全的读写


与实现中的所有其他方法一样,
putIfAbsent
锁定密钥的区域,而不是整个
Map
,因此允许同时在其他区域进行其他操作。

当多个线程可以同时访问同一映射时,使用ConcurrentHashMap。在这种情况下,手动实现putIfAbsent()是不可接受的,如下所示:

if (!map.containsKey(key)) {
    map.put(key, value);
} 
实际上,两个线程可能并行执行上面的块并进入竞争条件,其中两个线程都首先测试是否缺少密钥,然后都将自己的值放入映射中,从而打破程序的不变量


因此,ConcurrentHashMap提供了
putIfAbsent()
操作,确保以原子方式完成此操作,从而避免竞争条件。

当多个线程可以同时访问同一映射时,将使用ConcurrentHashMap。在这种情况下,手动实现putIfAbsent()是不可接受的,如下所示:

if (!map.containsKey(key)) {
    map.put(key, value);
} 
实际上,两个线程可能并行执行上面的块并进入竞争条件,其中两个线程都首先测试是否缺少密钥,然后都将自己的值放入映射中,从而打破程序的不变量


ConcurrentHashMap因此提供了
putIfAbsent()
操作,确保以原子的方式完成此操作,从而避免竞争条件。

想象一下,我们需要一个惰性初始化的命名单例bean缓存。下面是一个基于ConcurrentHashMap的无锁实现:

ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();

<T> T getBean(String name, Class<T> cls) throws Exception {
    T b1 = (T) map.get(name);
    if (b1 != null) {
        return b1;
    }
    b1 = cls.newInstance();
    T b2 = (T) map.putIfAbsent(name, b1);
    if (b2 != null) {
        return b2;
    }
    return b1;
}
ConcurrentMap=new ConcurrentHashMap();
T getBean(字符串名,类cls)引发异常{
tb1=(T)map.get(name);
如果(b1!=null){
返回b1;
}
b1=cls.newInstance();
t2=(T)map.putIfAbsent(名称,b1);
如果(b2!=null){
返回b2;
}
返回b1;
}

请注意,它解决了与双重检查锁定相同的问题,但没有锁定。

想象一下,我们需要一个惰性初始化命名单例bean的缓存。下面是一个基于ConcurrentHashMap的无锁实现:

ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();

<T> T getBean(String name, Class<T> cls) throws Exception {
    T b1 = (T) map.get(name);
    if (b1 != null) {
        return b1;
    }
    b1 = cls.newInstance();
    T b2 = (T) map.putIfAbsent(name, b1);
    if (b2 != null) {
        return b2;
    }
    return b1;
}
ConcurrentMap=new ConcurrentHashMap();
T getBean(字符串名,类cls)引发异常{
tb1=(T)map.get(name);
如果(b1!=null){
返回b1;
}
b1=cls.newInstance();
t2=(T)map.putIfAbsent(名称,b1);
如果(b2!=null){
返回b2;
}
返回b1;
}

请注意,它解决了与双重检查锁定相同的问题,但没有锁定。

看看(i)
的分段以及方法
put(paramK,i,paramV,true)
的工作原理。我想
true
是值得一看的。看看
segmentFor(I)
以及
方法如何放置(paramK,I,paramV,true)
。我想这是值得一看的。