ConcurrentHashMap.newKeySet() 是 Java 8 引入的一个静态工厂方法,用于创建一个线程安全的 Set 集合。
✅ 1. 用途:创建线程安全的 Set
- 返回的是一个实现了
java.util.Set接口的对象。 - 底层由
ConcurrentHashMap支持(值部分固定为Boolean.TRUE或类似占位符)。 - 所有操作(如
add,remove,contains)都是线程安全的。
Set<String> threadSafeSet = ConcurrentHashMap.newKeySet();
threadSafeSet.add("hello");
threadSafeSet.contains("world"); // false
✅ 2. 为什么需要它?
标准的 HashSet 不是线程安全的,在多线程环境下可能导致:
- 数据丢失
ConcurrentModificationException- 内存可见性问题
虽然可以用 Collections.synchronizedSet(new HashSet<>()) 包装,但:
- 它使用全局锁,并发性能差
- 所有操作串行化,吞吐量低
而 newKeySet() 基于 ConcurrentHashMap,具有:
- 细粒度锁(JDK 8+ 使用 CAS + synchronized 锁单个桶)
- 高并发读写性能
- 无锁读操作(
get/contains完全不加锁)
✅ 3. 与 map.keySet() 的区别
| 方法 | 能否 add() |
是否独立 Set | 说明 |
|---|---|---|---|
ConcurrentHashMap.newKeySet() |
✅ 可以 | ✅ 独立 | 专为 Set 设计,可自由增删 |
map.keySet() |
❌ 抛 UnsupportedOperationException |
❌ 视图 | 是 Map 的键视图,不能直接添加元素 |
map.keySet(defaultValue) |
✅ 可以 | ⚠️ 关联 Map | 添加元素会自动插入 key → defaultValue 到原 Map |
✅ 推荐:如果只需要一个线程安全的 Set,优先用
newKeySet(),简洁且高效。
✅ 4. 典型应用场景
- WebSocket 连接管理(存储活跃连接)
- 缓存去重(记录已处理的请求 ID)
- 并发任务去重队列
- 多线程共享的唯一标识集合
// 示例:管理 WebSocket 客户端
private static final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
public void onOpen(WebSocketSession session) {
sessions.add(session);
}
public void onClose(WebSocketSession session) {
sessions.remove(session);
}
✅ 5. 性能优势总结
| 方案 | 线程安全 | 并发性能 | 适用场景 |
|---|---|---|---|
HashSet |
❌ 否 | 高(单线程) | 单线程 |
Collections.synchronizedSet |
✅ 是 | 低(全局锁) | 低并发 |
CopyOnWriteArraySet |
✅ 是 | 读高写低 | 读多写极少 |
ConcurrentHashMap.newKeySet() |
✅ 是 | 高(分段/CAS) | 高并发读写 ✅ 推荐 |
🔚 结论
ConcurrentHashMap.newKeySet() 是 Java 中创建高性能、线程安全 Set 的最佳实践之一,特别适合高并发、频繁读写的场景。它避免了传统同步包装的性能瓶颈,同时提供了完整的 Set 语义和线程安全保障。