ConcurrentHashMap 无法按预期工作

Val*_*ous 11 java concurrency hashmap concurrenthashmap java-8

我正在计算电子选举的选票,并且在我的初始版本中我只有一个政党。每个选民将有不同的线程,线程将更新给定政党的选票计数。

我决定使用 ConcurrentHashMap,但结果不是我所期望的......

Map<String, Integer> voting = new ConcurrentHashMap<>();

for (int i = 0; i < 16; i++) {
  new Thread(() -> {
    voting.put("GERB", voting.getOrDefault("GERB", 0) + 1);
  }).start();
}

for (int i = 0; i < 100; i++) {
  voting.put("GERB", voting.getOrDefault("GERB", 0) + 1);
}

Thread.sleep(5000); // Waits for the threads to finish

for (String s : voting.keySet()) {
  System.out.println(s + ": " + voting.get(s));
}
Run Code Online (Sandbox Code Playgroud)

结果每次都不一样——范围从 114 到 116。

ConcurrentHashMap 不应该同步吗?

Rav*_*ala 12

嗯,这里有一个复合动作。您获得给定键的映射值,将其递增 1,然后将其放回映射中的同一个键。您必须保证所有这些语句都以原子方式执行。但是给定的实现并没有强加这个先决条件。因此,您最终会遇到安全故障。

要解决此问题,您可以使用中merge定义的原子操作ConcurrentHashMap。整个方法调用以原子方式执行。这是它的外观。

Map<String, Integer> voting = new ConcurrentHashMap<>();

for (int i = 0; i < 16; i++)
    new Thread(() -> {
        voting.merge("GERB", 1, Integer::sum);
    }).start();

for (int i = 0; i < 100; i++)
    voting.merge("GERB", 1, Integer::sum);

Thread.sleep(5000); // Waits for the threads to finish

for (String s : voting.keySet())
    System.out.println(s + ": " + voting.get(s));
Run Code Online (Sandbox Code Playgroud)

运行此程序会产生以下输出:

日耳曼语:116