Java Hashmap – multithreading放

我们最近在我的工作中讨论过我们是否需要使用ConcurrentHashMap,或者我们是否可以在multithreading环境中使用常规HashMap。 HashMaps的参数是两个:它比ConcurrentHashMap快,所以我们应该尽可能使用它。 而ConcurrentModificationException显然只会在你修改时迭代Map时出现,所以“如果我们只从地图中PUT和GET,那么常规HashMap有什么问题?” 是争论。

我认为并发PUT操作或并发PUT和READ可能会导致exception,所以我组合了一个测试来certificate这一点。 测试很简单; 创建10个线程,每个线程将相同的1000个键值对一次又一次地写入地图5秒钟,然后打印生成的地图。

结果实际上很混乱:

 Length:1299 Errors recorded: 0 

我认为每个键值对在HashMap中都是唯一的,但是通过查看地图,我可以找到多个相同的Key-Value对。 我期待某种exception或损坏的键或值,但我没想到这一点。 这是怎么发生的?

这是我使用的代码,供参考:

 public class ConcurrentErrorTest { static final long runtime = 5000; static final AtomicInteger errCount = new AtomicInteger(); static final int count = 10; public static void main(String[] args) throws InterruptedException { List threads = new LinkedList(); final Map map = getMap(); for (int i = 0; i < count; i++) { Thread t = getThread(map); threads.add(t); t.start(); } for (int i = 0; i < count; i++) { threads.get(i).join(runtime + 1000); } for (String s : map.keySet()) { System.out.println(s + " " + map.get(s)); } System.out.println("Length:" + map.size()); System.out.println("Errors recorded: " + errCount.get()); } private static Map getMap() { Map map = new HashMap(); return map; } private static Map getConcMap() { Map map = new ConcurrentHashMap(); return map; } private static Thread getThread(final Map map) { return new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); long now = start; while (now - start < runtime) { try { for (int i = 0; i < 1000; i++) map.put("i=" + i, i); now = System.currentTimeMillis(); } catch (Exception e) { System.out.println("P - Error occured: " + e.toString()); errCount.incrementAndGet(); } } } }); } } 

您面临的问题似乎是TOCTTOU类问题。 (是的,这种bug经常发生,它有自己的名字。:))

在地图中插入条目时, 至少需要执行以下两项操作:

  1. 检查密钥是否已存在。
  2. 如果检查返回true,则更新现有条目,如果没有,则添加新条目。

如果这两个不是primefaces地发生的(就像它们在正确同步的映射实现中那样),那么几个线程可以得出结论:在步骤1中密钥尚不存在,但是当它们到达步骤2时,不再是真的了。 因此,多个线程将很乐意插入具有相同键的条目。

请注意,这不是唯一可能发生的问题,根据实施情况和您的可见性,您可以获得各种不同的意外故障。