我可以将 ConcurrentHashMap 与 Integer 一起用于线程安全计数器吗?

Can I use ConcurrentHashMap with Integer for thread safe counters?

我想要几个柜台,我可以通过名称来解决。所以,我可以这样实现:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> new Integer(0));
map.computeIfPresent("key", (k, v) -> v + 1);

它的代码是线程安全的吗?

我认为是的,因为ConcurrentHashMap我们有同步访问,并且设置新引用也是线程安全操作。其他线程会因为安全发布而看到此更改,当我们在 ConcurrentHashMap.

中保留存储桶锁时会发生这种情况

假设第一个语句 map.computeIfAbsent("key", k -> new Integer(0)); 发生在 "initialization time",然后有一堆线程执行形式 map.computeIfPresent("key", (k, v) -> v + 1); 的调用,是的,算法将是正确的(我明白你的意思了吗?)。

JDK 的最新版本保证调用 ConcurrentHashMap.computeIfPresent() 不仅会调用以线程安全方式传入的表达式,它还会保证如果其他线程试图对同时使用相同的密钥,它们将阻塞并排队,这样所有的突变都会按顺序发生(这在分布式系统的术语中称为可串行化)。

是的,它是线程安全的,你可以测试一下:

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadsafeExample {

    public static void main(String[] args) throws Exception {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        int count = 1000;
        AtomicInteger doneTasks = new AtomicInteger();
        Thread[] threads = new Thread[count];
        for(int i = 0 ; i < threads.length ; ++i) {
            threads[i] = new Thread(() -> {
                map.computeIfAbsent("key", k -> new Integer(0));
                map.computeIfPresent("key", (k, v) -> v + 1);
                doneTasks.incrementAndGet();
            });
        }
        for(int i = 0 ; i < threads.length ; ++i)
            threads[i].start();

        while (doneTasks.get() < count)
            Thread.sleep(3);

        System.out.println("we expected count of key is: " + count + ", and we get: " + map.get("key"));
    }

}

输出:

we expected count of key is: 1000, and we get: 1000

您可以替换:

map.computeIfAbsent("key", k -> new Integer(0));
map.computeIfPresent("key", (k, v) -> v + 1);

来自

map.compute("key", (k, v) -> v == null ? 1 : v + 1);