Java中的ConcurrentHashMap?

时间:2010-05-14 17:35:04

标签: java concurrenthashmap

Java中ConcurrentHashMap的用途是什么?它有什么好处?它是如何工作的? 示例代码也很有用。

6 个答案:

答案 0 :(得分:68)

重点是提供线程安全的HashMap实现。多个线程可以读取和写入它,而不会接收过时或损坏的数据。 ConcurrentHashMap提供了自己的同步,因此您不必显式同步对它的访问。

ConcurrentHashMap的另一个功能是它提供putIfAbsent方法,如果指定的键不存在,原子添加映射。请考虑以下代码:

ConcurrentHashMap<String, Integer> myMap = new ConcurrentHashMap<String, Integer>();

// some stuff

if (!myMap.contains("key")) {
  myMap.put("key", 3);
}

此代码不是线程安全的,因为另一个线程可以在"key"的调用和contains的调用之间添加put的映射。正确的实施将是:

myMap.putIfAbsent("key", 3);

答案 1 :(得分:27)

ConcurrentHashMap允许并发访问地图。 HashTables也提供对地图的同步访问,但您的整个地图都被锁定以执行任何操作。

ConcurrentHashMap背后的逻辑是your entire table is not getting locked,但只有[segments]部分。每个细分都管理自己的HashTable。锁定仅适用于更新。在检索的情况下,它允许完全并发。

让我们将四个线程同时处理容量为32的映射,该表被分为四个段,每个段管理一个容量哈希表。该集合默认维护一个包含16个段的列表,每个段用于保护(或锁定)地图的单个存储桶。

enter image description here

这实际上意味着16个线程可以一次修改集合。使用可选的 concurrencyLevel构造函数参数可以增加此并发级别。

public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel)

正如另一个答案所述,ConcurrentHashMap提供了与put类似的新方法putIfAbsent(),但如果密钥存在则不会覆盖该值。

private static Map<String,String> aMap =new ConcurrentHashMap<String,String>();

if(!aMap.contains("key"))
   aMap.put("key","value");

新方法也更快,因为它避免了上述double traversingcontains方法必须找到段并迭代表以查找键,并且方法put必须再次遍历存储桶并放入密钥。

答案 2 :(得分:12)

实际上,最大的功能区别在于,当您使用它时,当其他人更改它时,它不会抛出异常和/或最终损坏。

对于常规集合,如果另一个线程在您访问它时(通过迭代器)添加或删除元素,它将引发异常。 ConcurrentHashMap允许他们进行更改而不会停止你的线程。

请注意,它不会对从一个线程到另一个线程的更改的时间点可见性做出任何形式的同步保证或承诺。 (它有点像读取提交的数据库隔离,而不是同步映射,其行为更像是可序列化的数据库隔离。(旧学校行锁定SQL可序列化,而不是Oracle-ish multiversion serializable :))

我所知道的最常见的用途是在App Server环境中缓存不可变的派生信息,其中许多线程可能正在访问相同的东西,如果两个碰巧计算相同的缓存值并将其放置两次并不重要因为它们交错等等(例如,它在Spring WebMVC框架内广泛使用,用于保存运行时派生的配置,如从URL到Handler方法的映射。)

答案 3 :(得分:4)

它可以用于记忆:

import java.util.concurrent.ConcurrentHashMap;
public static Function<Integer, Integer> fib = (n) -> {
  Map<Integer, Integer> cache = new ConcurrentHashMap<>();
  if (n == 0 || n == 1) return n;
  return cache.computeIfAbsent(n, (key) -> HelloWorld.fib.apply(n - 2) + HelloWorld.fib.apply(n - 1));
};

答案 4 :(得分:1)

1.ConcurrentHashMap是线程安全的,一次只能由单个线程访问代码。

2.ConcurrentHashMap同步或锁定Map的某个部分。为了优化ConcurrentHashMap的性能,Map根据并发级别划分为不同的分区。这样我们就不需要同步整个Map对象了。

3.默认并发级别为16,因此映射分为16个部分,每个部分由不同的锁管理,这意味着16个线程可以运行。

4.ConcurrentHashMap不允许NULL值。所以在ConcurrentHashMap中键不能为空。

答案 5 :(得分:0)

今天大家好,我们讨论了ConcurrentHashMap。
什么是ConcurrentHashMap?

ConcurrentHashMap是它在Java 1.5中引入的类,该类实现ConcurrentMap以及Serializable接口。 ConcurrentHashMap在处理多个Theading时增强了HashMap。 众所周知,当应用程序具有多线程时,HashMap不是一个好选择,因为会发生性能问题。

ConcurrentHashMap有一些关键点。

  • ConcurrentHashMap的底层数据结构是HashTable。
  • ConcurrentHashMap是一个类,该类是线程安全的,这意味着多个线程可以在单个线程对象上访问而没有任何复杂性。
  • ConcurretnHashMap对象根据并发级别划分为段数。
  • ConcurrentHashMap的默认并发级别为16。
  • 在ConcurrentHashMap中,任意数量的Thread都可以执行检索操作,但是对于对象中的更新,Thread必须锁定要在其中操作线程的特定Segment。
  • 这种类型的锁定机制称为段锁定或桶锁定。
  • 在ConcurrentHashMap中,一次执行16个更新操作。
  • 在ConcurrentHashMap中不能插入空值。

这是ConcurrentHashMap的构造。

  1. ConcurrentHashMap m = new ConcurrentHashMap();:使用默认的初始容量(16),负载因子(0.75)和concurrencyLevel(16)创建一个新的空地图。

  2. ConcurrentHashMap m = new ConcurrentHashMap(int initialCapacity);:创建具有指定初始容量,默认加载因子(0.75)和concurrencyLevel(16)的新的空地图。

  3. ConcurrentHashMap m = new ConcurrentHashMap(int initialCapacity,float loadFactor);: 使用指定的初始容量和负载因子以及默认的concurrencyLevel(16)创建一个新的空地图。

  4. ConcurrentHashMap m = new ConcurrentHashMap(int initialCapacity,float loadFactor,int concurrencyLevel);:使用指定的初始容量,负载因子和并发级别创建一个新的空映射。

  5. ConcurrentHashMap m = new ConcurrentHashMap(Map m);:创建一个与给定地图具有相同映射的新地图。

ConcurretHashMap有一个名为 putIfAbsent(); 的方法,该方法会阻止存储重复密钥,请参考以下示例。

    import java.util.concurrent.*; 

     class ConcurrentHashMapDemo { 
     public static void main(String[] args) 
     { 
         ConcurrentHashMap m = new ConcurrentHashMap(); 
          m.put(1, "Hello"); 
          m.put(2, "Vala"); 
          m.put(3, "Sarakar"); 

         // Here we cant add Hello because 1 key 
         // is already present in ConcurrentHashMap object 

            m.putIfAbsent(1, "Hello"); 

         // We can remove entry because 2 key 
         // is associated with For value 

            m.remove(2, "Vala"); 

        // Now we can add Vala

            m.putIfAbsent(4, "Vala"); 


            System.out.println(m); 
      } 
}