我有一个ConcurrentHashMap,我在其中执行以下操作:
sequences = new ConcurrentHashMap<Class<?>, AtomicLong>();
if(!sequences.containsKey(table)) {
synchronized (sequences) {
if(!sequences.containsKey(table))
initializeHashMapKeyValue(table);
}
}
我的问题是 - 是否有必要进行额外的
if(!sequences.containsKey(table))
检查synschronized块内部,以便其他线程不会初始化相同的hashmap值?
也许检查是必要的,我做错了?我正在做的事似乎有点傻,但我认为这是必要的。
答案 0 :(得分:21)
ConcurrentHashMap上的所有操作都是线程安全的,但线程安全操作不可组合。你试图让原子成为一对操作:检查地图中的某些东西,如果它不在那里,就把东西放在那里(我假设)。所以你的问题的答案是是,你需要再次检查,你的代码看起来还不错。
答案 1 :(得分:17)
您应该使用ConcurrentMap
的{{3}}方法。
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;
}
对于我们中间的功能性纯粹主义者,上述内容可以简化(或可能是复杂化):
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);
}
答案 2 :(得分:4)
您无法使用 ConcurrentHashMap 获得排他锁定。在这种情况下,您最好使用Synchronized HashMap。
如果对象尚未存在,则已经有一个原子方法放入ConcurrentHashMap; putIfAbsent
答案 3 :(得分:1)
我看到你在那里做了什么;-)问题是你自己看到了吗?
首先,您使用了所谓的“双重检查锁定模式”。你有快速路径(第一个包含),如果它满足则不需要同步,慢速路径,因为你做复杂的操作必须同步。您的操作包括检查地图内是否有东西,然后放置/初始化它。因此,ConcurrentHashMap对于单个操作是线程安全的并不重要,因为您执行两个必须被视为单元的简单操作,因此这个同步块是正确的,实际上它可以通过其他任何内容进行同步,例如this
。 / p>
答案 4 :(得分:1)
在Java 8中,您应该能够用.computeIfAbsent
替换双重检查的锁:
sequences.computeIfAbsent(table, k -> initializeHashMapKeyValue(k));
答案 5 :(得分:0)
使用以下内容创建名为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<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();
}
}
}
您会看到计数变化如下所示:
{a = 2,b = 3}
总字数:6
以77毫秒完成
{a = 3,b = 3}
总字数:6
现在在第13行注释掉ConcurrentHashMap,取消注释它上面的行并再次运行程序。
你会看到确定性的计数。