使用并发哈希映射的同步块是否正确?
Is using a synchronised block of concurrent hash map correct?
据我了解,ConcurrentHashMap 将允许多个线程在同一哈希映射上读写 (add/remove),而不会出现并发哈希映射异常。
我有 4 个线程,每个线程都可以更新 hashmap。我不希望其他线程在当前线程更新 hashmap 时 write/update。
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);
}
}
}
});
}
这是正确的做法吗?没有同步块,我得到的值不正确。
是的,它是正确的(假设这是更新地图的唯一地方),但它效率低下,因为它同步而不是依赖于地图固有的非阻塞并发。
您应该改用 compute():
playerLoginCounterHashMap.compute(
"testPlayer",
(key, value) -> value == null ? 1 : value + 1);
或merge():
playerLoginCounterHashMap.merge(
"testPlayer",
1,
Integer::sum);
请注意,在存储每个用户长计数器的简单情况下,使用 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开始。
据我了解,ConcurrentHashMap 将允许多个线程在同一哈希映射上读写 (add/remove),而不会出现并发哈希映射异常。
我有 4 个线程,每个线程都可以更新 hashmap。我不希望其他线程在当前线程更新 hashmap 时 write/update。
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);
}
}
}
});
}
这是正确的做法吗?没有同步块,我得到的值不正确。
是的,它是正确的(假设这是更新地图的唯一地方),但它效率低下,因为它同步而不是依赖于地图固有的非阻塞并发。
您应该改用 compute():
playerLoginCounterHashMap.compute(
"testPlayer",
(key, value) -> value == null ? 1 : value + 1);
或merge():
playerLoginCounterHashMap.merge(
"testPlayer",
1,
Integer::sum);
请注意,在存储每个用户长计数器的简单情况下,使用 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开始。