我有一个具有多个端点的Counter
类。我想保留每个端点的“访问”次数。可以从不同的线程访问端点,因此我决定使用ConcurrentHashMap
。
这是我的代码,我创建了一个模拟此行为的类:
public class Counter {
Map<String, Integer> endpoints = new ConcurrentHashMap<>();
void load(String endpoint) {
endpoints.put(endpoint, endpoints.get(endpoint) + 1);
}
void accessEndpoint(String endpoint, int times, int numberOfThreads) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
for (int i = 0; i < times; i++) {
executor.submit(() -> load(endpoint));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
counter.endpoints.put("www.google.com", 2);
counter.accessEndpoint("www.google.com", 100, 10);
System.out.println(counter.endpoints.get("www.google.com"));
}
}
输出不一致。
预期:102
实际:95、100、102、66、100
最佳答案
endpoints.put(endpoint, endpoints.get(endpoint) + 1);
不是原子操作。
因此,两个线程可以get()
具有相同的数字(例如100),而put
具有相同的数字(101),从而使两个调用仅算作一个。
使用compute()
代替,它是原子的:
endpoints.compute(endpoint, (k,v)-> v+1);
或者如果您想始终从零开始而不进行初始化。
endpoints.merge(endpoint, 1, Integer::sum);