在 java 中处理读多写少场景,首选 concurrenthashmap 配合 set 视图(如 keyset()),而非直接使用 hashset。因为 hashset 是线程不安全的,在高并发读取时若发生写入会导致 concurrentmodificationexception 或数据不一致;而 concurrenthashmap 通过分段锁或 cas 机制保证高并发下的安全与性能。具体做法是实例化一个 concurrenthashmap<k object></k>,利用其 putifabsent() 方法实现原子性添加,并通过 keyset() 获取只读集合进行遍历。

为什么 HashSet 不适合高并发读多写少
HashSet 基于 HashMap 实现,内部没有同步机制。当多个线程同时读取和修改时,迭代器会抛出异常,且底层数组扩容可能导致死循环(JDK 7)或数据丢失。因此,必须使用并发容器替代。

import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
public class ConcurrentSetExample {
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
public boolean add(String key) {
// putIfAbsent 是原子操作,避免竞态条件
return map.putIfAbsent(key, new Object()) == null;
}
public Set<String> getKeys() {
// 返回的是视图,遍历时需小心并发修改
return map.keySet();
}
public static void main(String[] args) {
ConcurrentSetExample example = new ConcurrentSetExample();
example.add("item1");
example.add("item2");
System.out.println(example.getKeys());
}
}
如何高效遍历 ConcurrentHashMap 的 Set 视图
遍历 ConcurrentHashMap 的 keySet() 时,由于它是弱一致性视图,遍历时不会抛出 ConcurrentModificationException,但可能看不到最新插入的元素。对于读多写少场景,这种“最终一致性”通常可接受。若需强一致性快照,可使用 CopyOnWriteArraySet,但其写性能较差,仅适合极少写的场景。
立即学习“Java免费学习笔记(深入)”;

import java.util.concurrent.ConcurrentHashMap;
import java.util.Iterator;
import java.util.Set;
public class SafeIterationExample {
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
public void iterateSafe() {
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
// 处理 key,即使期间有其他线程修改 map,也不会抛异常
System.out.println(key);
}
}
public static void main(String[] args) {
SafeIterationExample example = new SafeIterationExample();
example.map.put("a", new Object());
example.iterateSafe();
}
}
putIfAbsent 在键冲突时的行为表现
当调用 putIfAbsent(key, value) 时,如果键已存在,它不会覆盖原值,而是返回已有的值,并返回 false(逻辑上表示未添加)。这确保了在并发环境下,第一个成功写入的值被保留,后续竞争者被忽略,符合“读多写少”中优先保障已有数据的语义。


















