Java中如何在put时阻塞ConcurrentHashMap的get操作且不锁住整张表
实现方案
核心思路
因为要求不对整张Map加synchronized锁,我们采用细粒度的锁控制实现需求,结合题目给定的「仅单线程更新」前提,不会出现多线程写冲突的问题,两种可选方案可以适配不同的业务场景:
方案一:Key级别的读写锁(性能最优,推荐)
仅对正在更新的key加锁,不影响其他key的正常读取,完全不会锁整张表:
- 额外维护一个锁容器,每个key对应独立的读写锁
- 读操作先拿对应key的读锁,读完成释放
- 写操作(put)先拿对应key的写锁,此时所有该key的读操作会被阻塞,直到写锁释放
完整代码实现
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; // 原有配置存储容器 ConcurrentHashMap<String, Config> configStore = new ConcurrentHashMap<>(); // 每个key对应独立的读写锁,实现细粒度控制 ConcurrentHashMap<String, ReentrantReadWriteLock> lockMap = new ConcurrentHashMap<>(); // 封装后的读操作 public Config getConfig(String configName) { // 不存在对应锁则自动初始化 ReentrantReadWriteLock lock = lockMap.computeIfAbsent(configName, k -> new ReentrantReadWriteLock()); lock.readLock().lock(); try { return configStore.get(configName); } finally { lock.readLock().unlock(); } } // 改造后的更新操作 void updateStore(String configName) { ReentrantReadWriteLock lock = lockMap.computeIfAbsent(configName, k -> new ReentrantReadWriteLock()); lock.writeLock().lock(); try { Config newConfig = generateNewConfig(); Config oldConfig = configStore.get(configName); if (newConfig.replaces(oldConfig)) { configStore.put(configName, newConfig); } } finally { lock.writeLock().unlock(); } }
方案二:全局读写锁(实现简单,适配全局阻塞场景)
如果需求要求任意put操作执行时,所有key的读请求都要阻塞,可以用全局读写锁实现,同样不需要对整张Map加synchronized锁,读写锁的性能远优于粗粒度的synchronized锁:
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; ConcurrentHashMap<String, Config> configStore = new ConcurrentHashMap<>(); // 全局读写锁,读多写少场景性能优异 ReentrantReadWriteLock globalLock = new ReentrantReadWriteLock(); public Config getConfig(String configName) { globalLock.readLock().lock(); try { return configStore.get(configName); } finally { globalLock.readLock().unlock(); } } void updateStore(String configName) { globalLock.writeLock().lock(); try { Config newConfig = generateNewConfig(); Config oldConfig = configStore.get(configName); if (newConfig.replaces(oldConfig)) { configStore.put(configName, newConfig); } } finally { globalLock.writeLock().unlock(); } }
内容的提问来源于stack exchange,提问作者seriousgeek
相关产品推荐
相关产品推荐

