使用ConcurrentHashMap,何时需要同步?

我有一个ConcurrentHashMap,我在其中执行以下操作:

sequences = new ConcurrentHashMap<Class, AtomicLong>(); if(!sequences.containsKey(table)) { synchronized (sequences) { if(!sequences.containsKey(table)) initializeHashMapKeyValue(table); } } 

我的问题是 – 是否没有必要额外做

 if(!sequences.containsKey(table)) 

检查synschronized块内部,以便其他线程不会初始化相同的hashmap值?

也许检查是必要的,我做错了? 我正在做的事似乎有点傻,但我认为这是必要的。

ConcurrentHashMap上的所有操作都是线程安全的,但线程安全操作不可组合。 你试图让primefaces成为一对操作:检查地图中的某些东西,如果它不在那里,就把东西放在那里(我假设)。 所以问题的答案是肯定的 ,你需要再次检查,你的代码看起来还不错。

您应该使用ConcurrentMap的putIfAbsent方法。

 ConcurrentMap map = new ConcurrentHashMap (); public long addTo(String key, long value) { // The final value it became. long result = value; // Make a new one to put in the map. AtomicLong newValue = new AtomicLong(value); // Insert my new one or get me the old one. AtomicLong oldValue = map.putIfAbsent(key, newValue); // Was it already there? Note the deliberate use of '!='. if ( oldValue != newValue ) { // Update it. result = oldValue.addAndGet(value); } return result; } 

对于我们中间的function纯粹主义者,上述内容可以简化(或者可能是复杂化):

 public long addTo(String key, long value) { return map.putIfAbsent(key, new AtomicLong()).addAndGet(value); } 

在Java 8中,我们可以避免不必要的AtomicLong创建:

 public long addTo8(String key, long value) { return map.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(value); } 

您无法使用ConcurrentHashMap获得独占锁定。 在这种情况下,您应该更好地使用Synchronized HashMap。

如果对象尚未存在,则已经有一个primefaces方法放入ConcurrentHashMap; putIfAbsent

我看到你在那里做了什么;-)问题是你自己看到了吗?

首先,您使用了一种名为“双重检查锁定模式”的东西。 你有快速路径(第一个包含),如果它满足则不需要同步,慢速路径,因为你做复杂的操作必须同步。 您的操作包括检查地图内是否有东西,然后放置/初始化它。 因此,ConcurrentHashMap对于单个操作是线程安全的并不重要,因为你做了两个简单的操作,必须将它们视为一个单元,所以这个同步块是正确的,实际上它可以被其他任何东西同步,例如this

在Java 8中,您应该能够使用.computeIfAbsent替换双重检查的锁:

 sequences.computeIfAbsent(table, k -> initializeHashMapKeyValue(k)); 

使用以下内容创建名为dictionary.txt的文件:

 a as an b bat ball 

这里我们有:以“a”开头的单词数:3

以“b”开头的单词计数:3

总字数:6

现在执行以下程序:java WordCount test_dictionary.txt 10

 public class WordCount { String fileName; public WordCount(String fileName) { this.fileName = fileName; } public void process() throws Exception { long start = Instant.now().toEpochMilli(); LongAdder totalWords = new LongAdder(); //Map wordCounts = Collections.synchronizedMap(new HashMap()); ConcurrentHashMap wordCounts = new ConcurrentHashMap(); Files.readAllLines(Paths.get(fileName)) .parallelStream() .map(line -> line.split("\\s+")) .flatMap(Arrays::stream) .parallel() .map(String::toLowerCase) .forEach(word -> { totalWords.increment(); char c = word.charAt(0); if (!wordCounts.containsKey(c)) { wordCounts.put(c, new LongAdder()); } wordCounts.get(c).increment(); }); System.out.println(wordCounts); System.out.println("Total word count: " + totalWords); long end = Instant.now().toEpochMilli(); System.out.println(String.format("Completed in %d milliseconds", (end - start))); } public static void main(String[] args) throws Exception { for (int r = 0; r < Integer.parseInt(args[1]); r++) { new WordCount(args[0]).process(); } } 

}

您会看到计数变化如下所示:

{a = 2,b = 3}

总字数:6

完成时间为77毫秒

{a = 3,b = 3}

总字数:6

现在在第13行注释掉ConcurrentHashMap,取消注释它上面的行并再次运行程序。

你会看到确定性的计数。