我想有几个计数器,我可以按名称寻址。所以,我可以这样实现它:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> new Integer(0));
map.computeIfPresent("key", (k, v) -> v + 1);
它是线程代码安全的吗?
我想是的,因为我们因为ConcurrentHashMap
而同步访问,并且设置新引用也是线程安全操作。由于安全发布,其他线程将看到这种情况发生变化,当我们在ConcurrentHashMap
中保留存储桶锁定时会发生这种情况。
是的,它是线程安全的,你可以测试:
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);
假设第一个语句map.computeIfAbsent("key", k -> new Integer(0));
发生在"初始化时",然后有一堆线程执行map.computeIfPresent("key", (k, v) -> v + 1);
形式的调用,是的,算法是正确的(我是否很好地理解了您的意图?
最新版本的 JDK 保证对ConcurrentHashMap.computeIfPresent()
的调用不仅会调用以线程安全方式传入的表达式,它还会保证如果其他线程尝试同时对同一个键进行操作,它们将阻塞并排队,以便所有突变按顺序发生(这在分布式系统术语中称为可序列化性(。