ConcurrentHashMap
ConcurrentHashMap
是 Java 中的一个线程安全的哈希表,它允许多个线程同时读写而不需要额外的同步开销。它通过使用分段锁(segment locks)来实现对哈希表的并发访问,从而提高了并发性能。
ConcurrentHashMap基本用法
-
创建 ConcurrentHashMap:
你可以指定初始容量和负载因子来创建一个ConcurrentHashMap
,也可以使用默认值。ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
-
添加元素:
使用putIfAbsent
方法可以安全地添加元素,如果键不存在,则添加;如果已存在,则返回已存在的值。String value = map.putIfAbsent(key, value);
-
读取元素:
直接通过键来获取值。String value = map.get(key);
-
更新元素:
使用computeIfPresent
方法可以原子地更新键对应的值。map.computeIfPresent(key, (k, v) -> newValue);
-
删除元素:
使用remove
方法可以删除指定的键值对。String value = map.remove(key);
-
遍历:
ConcurrentHashMap
允许在迭代过程中进行元素的添加、删除和更新操作。for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); // 进行操作 }
-
原子操作:
ConcurrentHashMap
提供了多种原子操作,如putIfAbsent
、computeIfAbsent
、compute
、merge
等。 -
大小和键值集合:
可以获取ConcurrentHashMap
的大小,键集合和值集合。int size = map.size(); Set<String> keys = map.keySet(); Collection<String> values = map.values();
-
清空:
使用clear
方法可以清空整个ConcurrentHashMap
。map.clear();
-
并发级别:
ConcurrentHashMap
的构造函数允许你指定一个并发级别,这个级别决定了内部锁的数量,从而影响并发性能。ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(16);
使用 ConcurrentHashMap
时,你不需要手动同步代码块,因为它的内部操作都是线程安全的。然而,如果你需要对多个操作进行复合操作的原子性保证,你可能需要使用 Collections.synchronizedMap
或者其他同步机制。
例子
这里是一个使用 ConcurrentHashMap
的具体例子,展示了如何在多线程环境下安全地更新和访问一个共享的映射(Map)。
假设我们有一个在线购物车系统,需要记录不同用户的购物车信息,并且允许多个用户同时修改他们的购物车。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ShoppingCartExample {
// 使用 ConcurrentHashMap 存储用户的购物车信息
private static ConcurrentHashMap<String, Integer> shoppingCarts = new ConcurrentHashMap<>();
// 模拟添加商品到购物车
public static void addToCart(String userId, int quantity) {
// 使用 computeIfAbsent 确保即使多个线程同时访问,也只会为 userId 创建一次购物车
shoppingCarts.computeIfAbsent(userId, k -> 0); // 默认数量为 0
// 使用 accumulate 方法原子地增加商品数量
for (int i = 0; i < quantity; i++) {
shoppingCarts.accumulateAndGet(userId, 1, Integer::sum);
}
}
// 模拟获取用户的购物车商品数量
public static int getCartSize(String userId) {
return shoppingCarts.getOrDefault(userId, 0);
}
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 模拟 5 个用户同时添加商品到购物车
for (int i = 0; i < 5; i++) {
int finalI = i;
executorService.submit(() -> {
for (int j = 0; j < 10; j++) {
addToCart("user" + finalI, 1); // 用户 user0 到 user4 各添加 10 个商品
}
});
}
// 等待所有任务完成
executorService.shutdown();
// 验证购物车大小
try {
if (executorService.awaitTermination(1, TimeUnit.MINUTES)) {
for (int i = 0; i < 5; i++) {
System.out.println("User " + i + " cart size: " + getCartSize("user" + i));
}
} else {
System.out.println("Tasks did not complete in time.");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中:
- 使用了
ConcurrentHashMap
来存储每个用户的购物车商品数量。 addToCart
方法通过computeIfAbsent
确保每个用户都有一个初始数量为 0 的购物车,然后通过accumulateAndGet
原子地增加商品数量。getCartSize
方法返回特定用户购物车的商品数量。- 在
main
方法中,创建了一个固定大小的线程池,并模拟了 5 个用户同时向他们的购物车添加商品的场景。 - 使用了
executorService.shutdown()
来关闭线程池,并等待所有任务完成。 - 最后,输出每个用户的购物车大小,以验证
ConcurrentHashMap
是否正确地处理了并发更新。
这个例子展示了 ConcurrentHashMap
如何在多线程环境中安全地处理并发读写操作,而无需额外的同步开销。