1. 什么是 ConcurrentHashMap?
ConcurrentHashMap 是 Java 中 线程安全的 HashMap,位于 java.util.concurrent 包下。
它解决了 HashMap 在多线程下不安全、Collections.synchronizedMap() 性能低的问题。
核心优势:高并发读写性能,读操作完全不加锁,写操作只锁部分数据。
2. 为什么不用 HashMap 或 synchronizedMap?
方案 | 问题 |
| 多线程下可能死循环(JDK 1.7)、数据错乱 |
| 所有操作都加 |
| ✅ 分段锁 / CAS + synchronized(JDK 1.8+),读不加锁,写并发高 |
3. 底层原理(JDK 1.8+)
数组 + 链表 + 红黑树(和
HashMap一样)CAS + synchronized:写操作使用
synchronized锁住链表头或红黑树根节点,粒度更小读操作不加锁:利用
volatile保证内存可见性
结果:高并发下性能远超 synchronizedMap
4. 案例 1:高频缓存系统(读多写少)
场景:用户信息缓存,频繁读取,偶尔更新。
import java.util.concurrent.ConcurrentHashMap;
public class UserCache {
// ✅ 线程安全的缓存
private static final ConcurrentHashMap<Long, User> userCache = new ConcurrentHashMap<>();
// 模拟数据库查询
private static User findUserFromDB(Long id) {
return new User(id, "用户" + id, 20 + id.intValue());
}
// 获取用户(高频读)
public static User getUser(Long id) {
User user = userCache.get(id);
if (user == null) {
user = findUserFromDB(id);
userCache.put(id, user); // 偶尔写
}
return user;
}
// 更新用户(低频写)
public static void updateUser(User user) {
userCache.put(user.getId(), user);
}
}优势:
多个线程同时
getUser()不互斥,性能极高put只锁住对应桶(bucket),不影响其他 key 的读写
5. 案例 2:计数统计(高并发写)
场景:统计每个商品的访问次数,多个线程同时增加计数。
public class ProductViewCounter {
// key: 商品ID, value: 访问次数
private static final ConcurrentHashMap<Long, Long> viewCount = new ConcurrentHashMap<>();
public static void incrementView(Long productId) {
// ✅ putIfAbsent + compute 原子操作
viewCount.compute(productId, (key, count) -> count == null ? 1 : count + 1);
}
// 获取访问量
public static Long getViewCount(Long productId) {
return viewCount.getOrDefault(productId, 0L);
}
}为什么用 compute?
compute是原子操作,避免:
Long count = viewCount.get(productId); viewCount.put(productId, count + 1); // 中间可能被其他线程修改
替代方案:LongAdder(更高性能)
private static final ConcurrentHashMap<Long, LongAdder> viewAdder = new ConcurrentHashMap<>();
public static void incrementView(Long productId) {
viewAdder.computeIfAbsent(productId, k -> new LongAdder()).increment();
}6. 案例 3:限流器(Rate Limiter)
场景:限制每个 IP 每秒最多 10 次请求。
public class RateLimiter {
// key: IP, value: 请求次数
private static final ConcurrentHashMap<String, Integer> requestCount = new ConcurrentHashMap<>();
private static final int LIMIT = 10;
public static boolean allowRequest(String ip) {
//原子性地增加计数
Integer count = requestCount.merge(ip, 1, Integer::sum);
return count <= LIMIT;
}
// 每秒清空一次(由定时任务调用)
public static void reset() {
requestCount.clear();
}
}merge(key, 1, Integer::sum):
如果 key 不存在,插入
1如果存在,执行
sum函数(oldValue + 1)
原子操作,线程安全
7. 案例 4:状态机管理
场景:订单状态流转,每个订单一个状态。
public class OrderStatusManager {
// key: 订单ID, value: 状态
private static final ConcurrentHashMap<String, String> orderStatus = new ConcurrentHashMap<>();
public static boolean changeStatus(String orderId, String expectedStatus, String newStatus) {
// CAS 风格更新
return orderStatus.replace(orderId, expectedStatus, newStatus);
}
// 初始化订单
public static void createOrder(String orderId) {
orderStatus.putIfAbsent(orderId, "CREATED");
}
public static String getStatus(String orderId) {
return orderStatus.get(orderId);
}
}putIfAbsent:如果不存在才插入,避免重复创建replace(key, expect, update):只有当前值等于expect才更新,类似compareAndSet
适合实现乐观锁式的状态变更。
8. 案例 5:连接池或资源池
场景:管理数据库连接,避免重复创建。
public class ConnectionPool {
private static final ConcurrentHashMap<String, Connection> pool = new ConcurrentHashMap<>();
private static final String DEFAULT_URL = "jdbc:mysql://localhost:3306/test";
public static Connection getConnection() {
return pool.computeIfAbsent(DEFAULT_URL, url -> {
System.out.println("创建新连接...");
return createConnection(url);
});
}
private static Connection createConnection(String url) {
// 模拟创建连接
return new Connection(url);
}
public static void closeConnection(String url) {
Connection conn = pool.remove(url);
if (conn != null) {
conn.close();
}
}
}computeIfAbsent:原子操作,确保只创建一次连接多个线程同时调用
getConnection(),也只会创建一个连接
9. 常用方法速查表
方法 | 用途 | 是否原子 |
| 获取值 | ✅ |
| 设置值 | ✅ |
| 不存在才插入 | ✅ |
| 删除 | ✅ |
| CAS 风格更新 | ✅ |
| 计算并更新 | ✅ |
| 合并值 | ✅ |
| 不存在时计算 | ✅ |
| 存在时计算 | ✅ |
| 获取大小(估算值) | ✅ |
注意:size() 在高并发下是估算值,因为它是遍历所有段累加的,过程中可能有变化。
10. 注意事项
优先使用
computeIfAbsent:避免get + put的竞态条件避免使用
size()做精确判断:如“如果 size < 100 才添加”,可能不准迭代时注意:
ConcurrentHashMap支持遍历中修改,但不保证反映最新的修改不要用
ConcurrentHashMap实现复杂逻辑:如果逻辑复杂,考虑用synchronized或ReentrantLock
11. 总结
场景 | 推荐方法 |
缓存读取 |
|
计数统计 |
|
限流控制 |
|
状态变更 |
|
资源池 |
|

0条评论
点击登录参与评论