14

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

sequences = new ConcurrentHashMap<Class<?>, AtomicLong>();

if(!sequences.containsKey(table)) {
    synchronized (sequences) {
        if(!sequences.containsKey(table))
            initializeHashMapKeyValue(table);
    }
}

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

if(!sequences.containsKey(table))

检查同步块内部,以便其他线程不会初始化相同的哈希图值?

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

4

6 回答 6

21

ConcurrentHashMap 上的所有操作都是线程安全的,但线程安全的操作是不可组合的。您试图使一对原子操作:检查地图中的某些东西,如果它不存在,则将某些东西放在那里(我假设)。所以你的问题的答案是肯定的,你需要再次检查,你的代码看起来没问题。

于 2013-02-13T10:48:26.703 回答
17

您应该使用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;
}

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

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);
}
于 2013-02-13T10:51:16.440 回答
4

您无法使用ConcurrentHashMap获得排他锁。在这种情况下,您最好使用 Synchronized HashMap。

如果对象不存在,则已经有一个原子方法可以放在 ConcurrentHashMap 中;putIfAbsent

于 2013-02-13T10:42:29.917 回答
1

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

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

于 2013-02-13T10:51:08.110 回答
1

在 Java 8 中,您应该能够将双重检查锁替换为.computeIfAbsent

sequences.computeIfAbsent(table, k -> initializeHashMapKeyValue(k));
于 2014-12-23T06:19:46.483 回答
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,取消注释上面的行并再次运行程序。

您会看到确定性计数。

于 2017-03-17T09:50:16.693 回答