使用并发哈希映射的同步块是否正确?

时间:2017-08-28 12:47:13

标签: java concurrency concurrenthashmap

根据我的理解,ConcurrentHashMap将允许多个线程在同一个哈希映射上读取和写入(添加/删除),而不会获得并发哈希映射异常。

我有4个线程,每个线程都可以更新hashmap。当前线程正在更新时,我不希望其他线程在hashmap上编写/更新。

ConcurrentHashMap<String, Integer> playerLoginCounterHashMap = new ConcurrentHashMap<>();

    ExecutorService executorService = Executors.newFixedThreadPool(4);

    for (int i = 0; i < 4; i++) {

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                synchronized (playerLoginCounterHashMap) {
                    if (playerLoginCounterHashMap.get("testPlayer") == null) {
                        playerLoginCounterHashMap.put("testPlayer", 1);
                    } else {
                        playerLoginCounterHashMap.put("testPlayer", playerLoginCounterHashMap.get("testPlayer").intValue() + 1);
                    }
                }
            }
        });
    }

这是正确的做法吗?如果没有synchronized块,我会得到不正确的值。

2 个答案:

答案 0 :(得分:2)

是的,它是正确的(假设这是地图更新的唯一地方),但它效率低,因为它同步而不是依赖于地图固有的非阻塞并发性。

您应该使用compute()代替:

playerLoginCounterHashMap.compute(
    "testPlayer",
    (key, value) -> value == null ? 1 : value + 1);

merge()

playerLoginCounterHashMap.merge(
    "testPlayer",
    1,
    Integer::sum);

答案 1 :(得分:1)

请注意,在存储每个用户长计数器的简单情况下,使用Google Guava AtomicLongMap可能有意义:

final AtomicLongMap<String> loginCounterByPlayerName = AtomicLongMap.create();
final ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 4; i++) {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            loginCounterByPlayerName.addAndGet("testPlayer", 1);
        }
    });
}

唯一不同的是,计数器从0开始。