java 使用 ConcurrentHashMap,什么时候需要同步?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14851624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using ConcurrentHashMap, when is synchronizing necessary?
提问by Per Stilling
I have a ConcurrentHashMap where I do the following:
我有一个 ConcurrentHashMap,我在其中执行以下操作:
sequences = new ConcurrentHashMap<Class<?>, AtomicLong>();
if(!sequences.containsKey(table)) {
synchronized (sequences) {
if(!sequences.containsKey(table))
initializeHashMapKeyValue(table);
}
}
My question is - is it unnecessary to make the extra
我的问题是 - 有没有必要做额外的
if(!sequences.containsKey(table))
Check inside the synschronized block so other threads wont initialize the same hashmap value?
检查同步块内部,以便其他线程不会初始化相同的哈希图值?
Maybe the check is necessary and I am doing it wrong? It seems a bit silly what I'm doing, but I think it is necessary.
也许检查是必要的,而我做错了?我在做什么似乎有点愚蠢,但我认为这是必要的。
回答by Jo?o Fernandes
Alloperations on a ConcurrentHashMap are thread-safe, but thread-safe operations are not composable. You trying to make atomic a pair of operations: checking for something in the map and, in case it's not there, put something there (I assume). So the answer to your questions is yes, you need to check again, and your code looks ok.
ConcurrentHashMap 上的所有操作都是线程安全的,但线程安全的操作是不可组合的。您试图使原子操作成为一对:检查地图中的某些东西,如果它不存在,则将某些东西放在那里(我假设)。所以你的问题的答案是肯定的,你需要再次检查,你的代码看起来没问题。
回答by OldCurmudgeon
You should be using the putIfAbsentmethods of ConcurrentMap
.
您应该使用 的putIfAbsent方法ConcurrentMap
。
ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong> ();
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;
}
For the functional purists amongst us, the above can be simplified (or perhaps complexified) to:
对于我们中间的功能纯粹主义者,上述内容可以简化(或复杂化)为:
public long addTo(String key, long value) {
return map.putIfAbsent(key, new AtomicLong()).addAndGet(value);
}
And in Java 8 we can avoid the unnecessary creation of an AtomicLong
:
在 Java 8 中,我们可以避免不必要的创建AtomicLong
:
public long addTo8(String key, long value) {
return map.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(value);
}
回答by rai.skumar
You can't get exclusive lock with ConcurrentHashMap. In such case you should better use Synchronized HashMap.
您无法使用ConcurrentHashMap获得排他锁。在这种情况下,您最好使用 Synchronized HashMap。
There is already an atomicmethod to put inside ConcurrentHashMap if the object is not already there; putIfAbsent
如果对象不存在,则已经有一个原子方法可以放入 ConcurrentHashMap 中;putIfAbsent
回答by Marek
I see what you did there ;-) question is do you see it yourself?
我明白你在那里做了什么 ;-) 问题是你自己看到了吗?
First off all you used something called "Double checked locking pattern". Where you have fast path (first contains) which does not need synchronization if case it is satisfied and slow path which must be synchronized because you do complex operation. Your operation consists of checking if something is inside the map and then putting there something / initializing it. So it does not matter that ConcurrentHashMap is thread safe for single operation because you do two simple operations which must be treated as unit so yes this synchronized block is correct and actually it could be synchronized by anything else for example this
.
首先,您使用了称为“双重检查锁定模式”的东西。如果您有快速路径(第一个包含),如果情况满足则不需要同步,而慢路径必须同步,因为您执行复杂的操作。您的操作包括检查地图内是否有东西,然后在那里放置东西/初始化它。因此,ConcurrentHashMap 对于单个操作是线程安全的并不重要,因为您执行两个必须被视为单元的简单操作,所以是的,这个同步块是正确的,实际上它可以由其他任何东西同步,例如this
。
回答by Jonathan
In Java 8 you should be able to replace the double checked lock with .computeIfAbsent
:
在 Java 8 中,您应该能够将双重检查锁替换为.computeIfAbsent
:
sequences.computeIfAbsent(table, k -> initializeHashMapKeyValue(k));
回答by Kalyan Ghosh
Create a file named dictionary.txt with the following contents:
创建一个名为 dictionary.txt 的文件,内容如下:
a
as
an
b
bat
ball
Here we have: Count of words starting with "a": 3
我们有: 以“a”开头的单词数:3
Count of words starting with "b": 3
以“b”开头的单词数:3
Total word count: 6
总字数:6
Now execute the following program as: java WordCount test_dictionary.txt 10
现在执行以下程序: 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<Character, LongAdder> wordCounts = Collections.synchronizedMap(new HashMap<Character, LongAdder>());
ConcurrentHashMap<Character, LongAdder> wordCounts = new ConcurrentHashMap<Character, LongAdder>();
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();
}
}
}
}
You would see counts vary as shown below:
您会看到计数变化如下所示:
{a=2, b=3}
{a=2, b=3}
Total word count: 6
总字数:6
Completed in 77 milliseconds
77 毫秒内完成
{a=3, b=3}
{a=3, b=3}
Total word count: 6
总字数:6
Now comment out ConcurrentHashMap at line 13, uncomment the line above it and run the program again.
现在注释掉第 13 行的 ConcurrentHashMap,取消注释它上面的行并再次运行程序。
You would see deterministic counts.
您会看到确定性计数。