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

问题描述

我需要对多个资源实现线程安全的同步,每个资源一次只能被一个线程访问,但是可以同时访问不同的资源。我想出了以下代码,打算在 try-with-resources 语句中使用。

npx react-native start --reset-cache

代码一个问题是从未从 lockMap 中删除任何项目,而且我不知道如何以线程安全的方式执行此操作。以下绝对不是线程安全的:

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();
      }
   }
}

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,oldValue) -> oldValue == null ? 0 : --oldValue);
         if (claimsPerLock.get(key) <= 0)
         {
            lockMap.remove(key);
            claimsPerLock.remove(key);
         }
      }
   }
}