2022-12-20   


以前刚开始实现时,想当然的用ConcurrentHashMap的putIfAbsent和ReentrantLock来实现lock by key,但是一到后面仔细想想,发现有很多漏洞和麻烦的地方。

首先是错误的代码

            Map<String, Lock> keyLockMap = new ConcurrentHashMap();
            final Lock keyLock = keyLockMap.computeIfAbsent(k, k -> new ReentrantLock()); //1
            keyLock.lock(); //2
            try{
            //
             } finally {
                keyLock.unlock();
                keyLockMap.remove(username); //3
            }

会发生问题的地方

假设分别有3个线程现在运行到上述1,2,3位置处,那么3线程释放后,1获取的keyLock和2是两个不同的Lock对象,这里就会锁不住。
其实要考虑到资源释放,就没上面这边简单,起码还需要一个锁来保证资源的释放不和获取冲突。

网上搜索的简单的解决方式

hash分段锁

预先分配一批锁对象,利用hash索引去获取锁,这样就没有上面那个错误代码的问题了

  int concurrencyLevel = 1 << 8;   // 256 locks
  final Lock[] locks = new Lock[concurrencyLevel];
  // initialize locks

  void doSomething(String id) {
      Lock lock = locks[Math.abs(id.hashCode()) % concurrencyLevel];  // select one of 256 locks 
      lock.lock();
      try {
          // do some work
      } finally {
          lock.release();
      }
  }

现有的工具解决方法

Google Guava Striped

//init
stripes=Striped.lazyWeakLock(size);
//or
stripes=Striped.lock(size);
//...
Lock lock=stripes.get(object);

最后

以上都是堆内的解决方案,如果涉及到分布式,需要使用分布式的解决方案,比如Redisson

Q.E.D.


我并不是什么都知道,我只是知道我所知道的。