ConcurrentHashMap多线程场景下按值字段查询并安全插入方案问询
多线程下ConcurrentHashMap的线程安全实现:按值字段检查并插入
首先明确需求:根据给定的Object key(对应Record的a字段),查找是否存在匹配的Record,存在则返回对应的code(即Record的b字段,也是原Map的key);不存在则生成新code,插入ConcurrentHashMap后返回。原代码存在明显线程安全问题,具体问题如下:
原代码的线程安全隐患
- 遍历与插入非原子:多个线程可能同时遍历
entrySet都未找到匹配的Record,随后各自生成code并插入,导致同一个a字段对应多个code,数据重复。 - 弱一致性遍历:
ConcurrentHashMap的entrySet遍历是弱一致性的,遍历过程中其他线程的插入操作可能无法被当前遍历感知,导致漏查。
线程安全的实现方案
核心思路是利用ConcurrentHashMap的原子方法computeIfAbsent,同时维护一个以Record.a为key的映射,确保检查与插入操作的原子性。
实现代码
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; record Record(Object a, int b) {} public class SafeRecordStore { // 维护a字段到code的映射,用于快速查找 private final ConcurrentHashMap<Object, Integer> aToCodeMap = new ConcurrentHashMap<>(); // 维护code到Record的映射,保留原业务的Map结构需求 private final ConcurrentHashMap<Integer, Record> codeToRecordMap = new ConcurrentHashMap<>(); // 原子生成唯一code,避免重复冲突 private final AtomicInteger codeGenerator = new AtomicInteger(0); public int get(Object key) { // 先尝试从aToCodeMap快速查找 Integer existingCode = aToCodeMap.get(key); if (existingCode != null) { return existingCode; } // 原子化操作:仅当key不存在时,执行生成与插入逻辑 return aToCodeMap.computeIfAbsent(key, k -> { int newCode = generateUniqueCode(); codeToRecordMap.put(newCode, new Record(k, newCode)); return newCode; }); } // 生成全局唯一的code,可根据业务需求调整规则 private int generateUniqueCode() { return codeGenerator.getAndIncrement(); } }
关键说明
- 双Map维护:
aToCodeMap负责快速根据a字段查找code,避免全量遍历;codeToRecordMap保留原业务的Map结构,满足按code查找Record的需求。 - 原子性保证:
computeIfAbsent是ConcurrentHashMap的原子方法,多个线程同时调用时,只有一个线程会执行lambda中的生成与插入逻辑,其他线程会直接返回已生成的结果,避免重复插入。 - 唯一code生成:使用
AtomicInteger生成自增的code,确保全局唯一,避免与已有code冲突。
简化版实现(如果不需要按code查Record)
如果业务中不需要维护code到Record的映射,可直接将a字段作为ConcurrentHashMap的key,进一步简化代码:
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; record Record(Object a, int b) {} public class SimplifiedSafeStore { private final ConcurrentHashMap<Object, Record> aToRecordMap = new ConcurrentHashMap<>(); private final AtomicInteger codeGenerator = new AtomicInteger(0); public int get(Object key) { Record record = aToRecordMap.computeIfAbsent(key, k -> { int newCode = codeGenerator.getAndIncrement(); return new Record(k, newCode); }); return record.b(); } }
内容的提问来源于stack exchange,提问作者Question_283
相关产品推荐
相关产品推荐

