通过特定 ID 同步锁定

Synchronized lock by particular ID

我有一个 REST API,它有一个方法 M 可以做某事。
当然有时会被多个线程同时调用。
此方法 M 有一个输入字符串 businessID
(来自 caller/client 的有效载荷)。

现在...我想保护方法 M 主体中的一个特定部分免受多个线程的同时执行。但我想捍卫它 只有当 我有两个线程 T1 和 T2 同时执行 same businessID 值。所以经过一番思考,我决定采用这种方法。

public M(){ 
    
    // non-critical work 1 
    
    String bid = businessID.intern();
    synchronized (bid){
        // do some critical work here 
    }
    
    // non-critical work 2 
    
}

这意味着我打算使用字符串 businessID 内部版本 作为我代码关键部分的锁。

这会按预期工作吗?我想是的……但我想绝对确定。

另外,有没有人有任何替代想法来实现这个?我想知道是否有一些现成的解决方案,比如 Java 中的惯用方法,而无需实现我自己的缓存、我自己的驱逐机制等

请注意,此同步造成的延迟并不让我担心。两个线程在同一本书中调用具有相同业务 ID 的方法 M 的情况非常罕见(每天只发生一次或两次)。此外,关键部分完成执行所需的时间不超过 1-2 秒。所以等待获取锁的线程造成的延迟,这不是我担心的。

这似乎是个坏主意,因为:

  1. String#intern() 是一种本机方法,它使用本机 Hash Table,显然比典型的 ConcurrentHashMap much slower
  2. 您可能不希望字符串池无限增长。您将如何使那里的条目无效?

我认为使用您自己的字符串映射将是首选方式。也许可以利用 Guava 的缓存,因为您最终需要从该地图中逐出项目。但这需要进一步研究。

租一把锁

另一种选择是拥有一组预定义的锁对象。例如。大小为 513 的 HashMap。然后使用 bid.hashCode() mod 513:

获取锁
int hash = Math.abs(bid.hashCode() % 513);
Object lock = locks.get(hash);
synchronized(lock) {...}

这偶尔会锁定不相关的交易,但至少您不必为逐出而烦恼。

PS: 在数学 class.

中有一些方法可以计算出真正的 mod

我认为您可以维护每个 businessId 所持有的当前锁的注册表,并在开始临界区之前查看此注册表以获得 get/create 锁,并在完成临界区后释放锁。

好吧,它还没有准备好生产,如下所示

进口java.util.HashMap; 导入 java.util.Map;

class Lock {
    public static void main(String[] args) {
        String businessId ="bid1";
        Lock lock = getLockObjectForBusinessId(businessId);
       
        synchronized (lock) {
            //critical section start
            //do work
            //critical section end
            releaseLockForBusinessId(businessId);
        }

    }

    public static Map<String, Lock> currentLocks = new HashMap<>();

   public static synchronized Lock getLockObjectForBusinessId(String businessId){

       Lock currentLock = currentLocks.get(businessId);
       if(currentLock==null){
           Lock lock = new Lock();
           currentLocks.put(businessId,lock);
           return  lock;
       }
       else{
           return currentLock;
       }
    }

    public static synchronized  void releaseLockForBusinessId(String businessId){
        currentLocks.remove(businessId);
    }
}