线程安全锁定/同步到多个资源的一个资源许可



我需要实现对多个资源的线程安全同步,其中每个资源可以由一个线程一次访问,但不同的资源可以并发访问。我编写了下面的代码,用于try-with-resources语句。

public class Gatekeeper implements AutoCloseable
{
private static final ConcurrentMap<Long, ReentrantLock> lockMap = new ConcurrentHashMap<>();
private final        ReentrantLock                      lock;
private final        Long                               key;
public Gatekeeper(Long key)
{
this.key = key;
lock = lockMap.computeIfAbsent(key, (Long absentKey) -> new ReentrantLock(true)); // computeIfAbsent is an atomic operation
try
{
lock.tryLock(30, TimeUnit.SECONDS);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new Something(":(", e);
}
}
@Override
public void close()
{
if(lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}

这段代码的一个问题是没有项从lockMap中删除,我不知道如何做到线程安全。下面的代码肯定不是线程安全的:

@Override
public void close()
{
if (lock.isHeldByCurrentThread())
{
if (lock.getQueueLength() == 1) // todo: getQueueLength is meant for system monitoring purposes only
{
lockMap.remove(key); // todo: not thread-safe, queuelength could have changed by now
}
lock.unlock();
}
}

getQueueLength:

返回等待的线程数的估计值获取此锁。这个值只是一个估计值,因为当此方法遍历时,线程可能会动态变化内部数据结构。这种方法是为……设计的监视系统状态,而不是用于同步控制。

有谁知道这个问题的解决方案吗?是否有不同的策略来实现我的目标?

经过更多的实验,我想出了下面的代码,谁能评论这是否是一个好方法,代码是正确的?

public class Gatekeeper implements AutoCloseable
{
private static final ConcurrentMap<Long, ReentrantLock> lockMap = new ConcurrentHashMap<>();
private final        ReentrantLock                      lock;
private final        Long                               key;
private static final ConcurrentMap<Long, Integer> claimsPerLock = new ConcurrentHashMap<>();
private static final Object                       mutex   = new Object();

public Gatekeeper(Long key)
{
this.key = key;

synchronized (mutex)
{
lock = lockMap.computeIfAbsent(key, (Long absentKey) -> new ReentrantLock(true));
claimsPerLock.compute(key, (k, oldValue) -> oldValue == null ? 1 : ++oldValue);
}
try
{
if(!lock.tryLock(30, TimeUnit.SECONDS))
{
throw new SomeException("Timeout occurred while trying to acquire lock");
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new SomeException("Interrupted", e);
}
}
@Override
public void close()
{
lock.unlock();
synchronized (mutex)
{
claimsPerLock.compute(key, (k, oldValue) -> oldValue == null ? 0 : --oldValue);
if (claimsPerLock.get(key) <= 0)
{
lockMap.remove(key);
claimsPerLock.remove(key);
}
}
}
}