通过特定 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 秒。所以等待获取锁的线程造成的延迟,这不是我担心的。
这似乎是个坏主意,因为:
- String#intern() 是一种本机方法,它使用本机 Hash Table,显然比典型的 ConcurrentHashMap much slower。
- 您可能不希望字符串池无限增长。您将如何使那里的条目无效?
我认为使用您自己的字符串映射将是首选方式。也许可以利用 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);
}
}
我有一个 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 秒。所以等待获取锁的线程造成的延迟,这不是我担心的。
这似乎是个坏主意,因为:
- String#intern() 是一种本机方法,它使用本机 Hash Table,显然比典型的 ConcurrentHashMap much slower。
- 您可能不希望字符串池无限增长。您将如何使那里的条目无效?
我认为使用您自己的字符串映射将是首选方式。也许可以利用 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);
}
}