2010-05-14 46 views
43

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

+0

有用http://javarevisited.blogspot.in/2013/02/concurrenthashmap-in-java-example-tutorial-working.html – roottraveller 2017-07-03 06:45:46

回答

58

重点是提供线程安全的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); 
+4

我会更多地描述它为“安全不同步”。它允许两个线程同时在内部进行垃圾处理,并承诺之后会以一致的状态结束。 – Affe 2010-05-14 17:42:50

+4

同步不是(外部)订购保证。 – danben 2010-05-14 17:47:58

+4

这并不是保证请求监视器的线程将按照请求的顺序接收它,但是它是一个事件 - 保证一旦一个线程获取监视器并开始修改映射,其他人就不会看到它,直到修改为止完成。在并发映射中,一个线程可以启动一个put,然后在很长一段时间内不再被调度,其他线程可以执行gets,这样就不会看到正在进行的put。 – Affe 2010-05-14 17:57:06

11

真正大的功能差异是它不抛出异常和/或结束时损坏别人,当你使用它改变它。

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

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

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

+1

这根本不是真的。 javadoc明确指定所有操作都是线程安全的。 – danben 2010-05-14 17:43:40

+0

所有的操作都是线程安全的,但是在承诺之前没有发生,就像使用同步映射一样。当您查看ConcurrentHashMap时看到的内容是最近完成的操作的结果。其中一些可能比你开始试图寻找时间晚得多。它的工作方式类似于读取提交的数据库隔离,而同步映射更像是可序列化的数据库隔离。 – Affe 2010-05-14 17:46:23

+0

优秀答案 – SuprF1y 2016-12-31 04:47:01

22

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

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

我们假设四个线程同时在容量为32的映射上工作,该表被划分为四个段,每段管理一个容量哈希表。该集合默认维护一个16个分段的列表,每个分段用于保护(或锁定)地图的一个桶。

enter image description here

实际上,这意味着16个线程可以在单一时间修改集合。使用可选的并发级别构造函数参数可以提高此并发级别。

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

由于对方回答说,在ConcurrentHashMap中提供了新的方法putIfAbsent()它类似于把除值,如果键存在不会被覆盖。

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

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

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

+0

默认大小是32,那么4个线程怎么能够创建大小为16的段呢?不会是8 – 2017-05-25 08:39:40

2

它可用于记忆化:

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)); 
}; 
0

1.ConcurrentHashMap是线程安全的,其代码可以由单个线程同时访问。

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

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

4.ConcurrentHashMap不允许NULL值。因此,ConcurrentHashMap中的键不能为空。