如何在方法参数值上提供同步?
应同步使用“相同”参数值A的所有方法调用。具有不同参数值的方法调用,例如即使有A的呼叫已经在等待,B也可以访问。 B的下一个并发呼叫也必须等待第一个B被释放。
我的用例:我希望在ID级别上同步对JPA实体的访问,但是希望避免悲观锁定,因为我需要一种队列。用于锁定的“密钥”旨在作为实体ID - 实际上是Java Long类型。
protected void entityLockedAccess(SomeEntity myEntity) {
//getId() returns different Long objects so the lock does not work
synchronized (myEntity.getId()) {
//the critical section ...
}
}
我读到了锁定对象,但我不确定它们在我的情况下是否适合。在顶层,我想管理对我的应用程序执行关键代码的特定REST调用。
谢谢,克里斯
据我所知,你基本上想要为每个SomeEntity
ID提供一个不同的,独特的锁。
你可以用Map<Integer, Object>
意识到这一点。
您只需将每个ID映射到一个对象。如果已有对象,则重用它。这看起来像这样:
static Map<Integer, Object> locks = new ConcurrentHashMap<>();
public static void main(String[] args)
{
int i1 = 1;
int i2 = 2;
foo(i1);
foo(i1);
foo(i2);
}
public static void foo(int o)
{
synchronized (locks.computeIfAbsent(o, k -> new Object()))
{
// computation
}
}
这将在地图中创建2个锁定对象,因为i1
的对象在第二个foo(i1)
调用中被重用。
问题是你根本不应该同步值(例如字符串或Integer对象)。
含义:你需要在这里定义一些特殊的EntityId类,当然,所有使用相同ID的“数据”都需要使用相同的EntityId对象。
汇集和可能重用的对象不应用于同步。如果是,则可能导致无关的线程因无用的堆栈跟踪而死锁。
具体来说,String
文字和盒装基元(如Integers
)不应该用作锁定对象,因为它们被合并并重复使用。
Boolean
对象的情况更糟,因为Boolean
,Boolean.TRUE
和Boolean.FALSE
只有两个实例,每个使用布尔值的类都将指代其中一个。
我读到了锁定对象,但我不确定它们在我的情况下是否适合。在顶层,我想管理对我的应用程序执行关键代码的特定REST调用。
您的数据库将负责并发写入和其他事务问题。您所需要做的就是使用交易。
我还建议你去解决经典问题(DIRTY READs NON Repeatable reads)。您也可以使用Optimistic Locking
private static final Set<Integer> lockedIds = new HashSet<>();
private void lock(Integer id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Integer id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void entityLockedAccess(SomeEntity myEntity) throws InterruptedException {
try {
lock(myEntity.getId());
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(myEntity.getId());
}
}