You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为唯一字符串值实现多排他锁?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;
        }
    }
}

关键细节说明

  1. 重入支持:通过reentrantCount记录同一线程对同一key的加锁次数,符合常规锁的使用逻辑。
  2. 原子性保障:使用ConcurrentHashMap.computeIfAbsent确保每个key对应的锁条目仅被创建一次,避免并发冲突。
  3. 异常安全:在lock和unlock方法中添加异常处理,防止因异常导致锁泄漏或状态错乱。
  4. 内存优化:当重入计数归0时,从Map中移除对应条目,避免长期持有不再使用的锁实例。
  5. 合法性校验:严格校验解锁时的锁状态与当前线程,完全符合接口要求的异常抛出规则。

内容的提问来源于stack exchange,提问作者Андрей Щеглов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:40:57