0

我在 Java 7 recipes to update and iterate a Map 同时看到了这段代码:

ConcurrentMap<Integer,String> concurrentMap = new ConcurrentHashMap<Integer, String>();
for (int i =0;i < 1000;i++) {
  startUpdateThread(i, concurrentMap);
}
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  e.printStackTrace();
}
for (Map.Entry<Integer, String> entry : concurrentMap.entrySet()) {
  System.out.println("Key :"+entry.getKey()+" Value:"+entry.getValue());
}

以及更新地图的方法如下:

private void startUpdateThread(int i, final ConcurrentMap<Integer, String> concurrentMap) {
    Thread thread = new Thread(new Runnable() {
        public void run() {
            while (!Thread.interrupted()) {
                int randomInt = random.nextInt(20);
                concurrentMap.put(randomInt, UUID.randomUUID().toString());
            }
} });
    thread.setName("Update Thread "+i);
    updateThreads.add(thread);
    thread.start();
}

我只尝试了一个 HashMap 而不是 ConcurrentHasMap,结果是一样的(用 Java VisualVM 监控)。有谁知道为什么?

谢谢,奥古斯丁

更新:几个有趣的问题:

  1. 如果 HashMap 的容量是恒定的,那么以下哪些操作可以安全地执行?
    • 两个线程更新 HashMap 中的值。
    • 两个线程更新 HashMap 中的值,而第三个线程正在读取 HashMap。
  2. 如果我想加快更新地图的速度,那么在只有 4 个处理器的计算机中拥有超过 4 个线程是否有意义?
4

2 回答 2

2

ConcurrentHashMap允许多线程访问,相比之下HashMap,不允许。

HashMap#put同时从多个线程调用可能会破坏您的地图。ConcurrentHashMap处理这些情况并减轻竞争条件。

特别是对于您的测试,您的地图只有 20 个键,这意味着它会相对较快地填满。hashmap 的弱点是当你需要扩展存储桶空间的同时又放另一个条目时。尝试增加键的数量,Integer.MAX_VALUE您将有更高的机会看到它损坏。

于 2013-10-27T10:15:00.590 回答
1

下面的代码会给你一个不同的结果。您的代码不会崩溃的原因是 20 个整数的小随机间隔。因此,您的地图会立即变满,并且在此之后它的键集不会被修改。我将随机间隔更改为 10000 并添加了睡眠,所以现在它在程序运行期间不断更新,并在HashMap. 它适用于ConcurrentHashMap能够迭代正在修改的数据。

public class Test {
    static List<Thread> threadList = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        Map<Integer,String> map = new HashMap<>();
        for (int i =0;i < 1000;i++) {
            startUpdateThread(i, map);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println("Key :"+entry.getKey()+" Value:"+entry.getValue());
        }

        for (Thread thread : threadList) {
            thread.interrupt();
        }
    }

    private  static void startUpdateThread(int i, final Map<Integer, String> concurrentMap) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                Random random = new Random();
                while (true) {
                    int randomInt = random.nextInt(10000);
                    concurrentMap.put(randomInt, UUID.randomUUID().toString());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            } });

        threadList.add(thread);

        thread.setName("Update Thread " + i);
        thread.start();
    }
}
于 2013-10-27T10:37:15.510 回答