如何为唯一字符串值实现多排他锁?Java并发实现咨询
基于字符串Key的独占锁实现方案
适合的并发原语
从java.util.concurrent包中,以下原语最适配该需求:
ReentrantLock:支持独占式锁定与可重入特性,能记录当前持有锁的线程,便于解锁时的合法性校验;同时支持响应中断和超时等待,满足lock方法的阻塞等待要求。ConcurrentHashMap:线程安全的哈希表,用于动态存储字符串key与对应锁实例的映射,支持原子性的添加/移除操作,完美适配动态生成key的场景,避免并发下的Map操作冲突。
具体实现
我们需要封装一个锁条目(LockEntry)来关联ReentrantLock、持有线程和重入计数,再通过ConcurrentHashMap管理这些条目。实现类如下:
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nonnull; interface Lock { void lock(@Nonnull String key); void unlock(@Nonnull String key); } public class KeyBasedLock implements Lock { // 线程安全存储key对应的锁条目 private final ConcurrentHashMap<String, LockEntry> lockMap = new ConcurrentHashMap<>(); // 封装锁实例、持有线程和重入计数 private static class LockEntry { final ReentrantLock lock = new ReentrantLock(); Thread holderThread; int reentrantCount; } @Override public void lock(@Nonnull String key) { // 原子性获取或创建锁条目,避免并发重复创建 LockEntry entry = lockMap.computeIfAbsent(key, k -> new LockEntry()); // 获取锁,支持响应中断(若需超时可替换为tryLock(long, TimeUnit)) entry.lock.lock(); try { // 更新持有线程与重入计数 if (entry.holderThread == Thread.currentThread()) { entry.reentrantCount++; } else { entry.holderThread = Thread.currentThread(); entry.reentrantCount = 1; } } catch (Throwable t) { // 加锁失败时释放锁,避免异常导致锁泄漏 entry.lock.unlock(); throw t; } } @Override public void unlock(@Nonnull String key) { LockEntry entry = lockMap.get(key); // 异常场景1:解锁未被锁定的key if (entry == null || entry.reentrantCount == 0) { throw new IllegalMonitorStateException("Key '" + key + "' is not locked"); } // 异常场景2:解锁被其他线程锁定的key if (entry.holderThread != Thread.currentThread()) { throw new IllegalMonitorStateException("Current thread does not hold the lock for key '" + key + "'"); } entry.reentrantCount--; try { if (entry.reentrantCount == 0) { // 重入计数归0时,移除条目并清空持有线程标记 lockMap.remove(key); entry.holderThread = null; } entry.lock.unlock(); } catch (Throwable t) { // 释放锁异常时恢复计数,避免状态不一致 if (entry.reentrantCount == 0) { entry.reentrantCount++; entry.holderThread = Thread.currentThread(); } throw t; } } }
关键细节说明
- 重入支持:通过
reentrantCount记录同一线程对同一key的加锁次数,符合常规锁的使用逻辑。 - 原子性保障:使用
ConcurrentHashMap.computeIfAbsent确保每个key对应的锁条目仅被创建一次,避免并发冲突。 - 异常安全:在
lock和unlock方法中添加异常处理,防止因异常导致锁泄漏或状态错乱。 - 内存优化:当重入计数归0时,从Map中移除对应条目,避免长期持有不再使用的锁实例。
- 合法性校验:严格校验解锁时的锁状态与当前线程,完全符合接口要求的异常抛出规则。
内容的提问来源于stack exchange,提问作者Андрей Щеглов
相关产品推荐
相关产品推荐

