锁定多个对象
Lock on multiple objects
如何锁定多个项目?
考虑下面的例子,
Map<String, Account> accountMap = new HashMap<>(); // key=accountNumber,value=AccountObject
class Account{
private String accountNumber; // getter & setter
private double accountBalance; // getter & setter
}
我需要将资金从一个账户转移到另一个账户,所以我想有一个嵌套的同步块,但意识到它会导致死锁。
// bad code
synchronized(accountMap.get(accountNumber1)){
synchronized(accountMap.get(accountNumber2)){
// business logic
}
}
此外,我不想要单个锁,因为它会阻止处理一个事务的所有线程。如下所示
//bad code
Object mutex = new Object();
synchronized(mutex){
// business logic with accountNumber1 & accountNumber2
}
我该如何解决这个问题?我只需要为两个帐户对象维护锁。
此外,可能重复(但我想知道是否有不同的解决方案)。 Preventing deadlock in a mock banking database
您可以锁定两个对象并仍然使用锁定顺序避免死锁
if (accountNumber1 < accountNumber2) {
synchronized (accountMap.get(accountNumber1)) {
synchronized (accountMap.get(accountNumber2)) {
//
}
}
} else {
synchronized (accountMap.get(accountNumber2)) {
synchronized (accountMap.get(accountNumber1)) {
//
}
}
}
}
这样我们就避免了循环等待。
我假设你的 Hashmap 写入是线程安全的或者它是只读的。
无法获得多个对象的单个锁。但还有其他选择。
您可以使用共享(又名 "global")锁。问题是这个锁可能是一个瓶颈。
您可以使用其他某个对象作为这两个对象的代理,并获得对其的锁定。例如,假设您的帐户具有唯一 ID:
String lockName = (acc1.getAccountNumber() + "-"
+ acc2.getAccountNumber()).intern();
synchronized (lockName) {
// we now have a lock on the combination of acc1 and acc2.
}
(但是,这可能不适用于您的用例,因为它不会停止涉及两个帐户之一和第三个帐户的同时转账。)
按规范顺序获取锁。例如。
if (acc1.getAccountNumber().compareTo(acc2.getAccountNumber()) < 0) {
synchronized (acc1) {
synchronized (acc2) {
// do transfer
}
} else {
synchronized (acc2) {
synchronized (acc1) {
// do transfer
}
}
}
如果所有线程以相同的顺序获取锁,则不会发生死锁。
使用 Lock.tryLock
获取锁并加一次。然而,这有几个问题:
- 您现在需要管理每个帐户
Lock
个对象。
- 存在"livelock"的(理论)问题。要解决此问题,您可以使用随机生成的超时值。
(注意:不要尝试使用身份哈希码作为唯一 id 的代理。身份哈希码不是唯一的,而且您还会遇到内存中可能有多个的问题 Account
表示相同逻辑帐户的对象...如果它们是 DTO。)
如何锁定多个项目?
考虑下面的例子,
Map<String, Account> accountMap = new HashMap<>(); // key=accountNumber,value=AccountObject
class Account{
private String accountNumber; // getter & setter
private double accountBalance; // getter & setter
}
我需要将资金从一个账户转移到另一个账户,所以我想有一个嵌套的同步块,但意识到它会导致死锁。
// bad code
synchronized(accountMap.get(accountNumber1)){
synchronized(accountMap.get(accountNumber2)){
// business logic
}
}
此外,我不想要单个锁,因为它会阻止处理一个事务的所有线程。如下所示
//bad code
Object mutex = new Object();
synchronized(mutex){
// business logic with accountNumber1 & accountNumber2
}
我该如何解决这个问题?我只需要为两个帐户对象维护锁。
此外,可能重复(但我想知道是否有不同的解决方案)。 Preventing deadlock in a mock banking database
您可以锁定两个对象并仍然使用锁定顺序避免死锁
if (accountNumber1 < accountNumber2) {
synchronized (accountMap.get(accountNumber1)) {
synchronized (accountMap.get(accountNumber2)) {
//
}
}
} else {
synchronized (accountMap.get(accountNumber2)) {
synchronized (accountMap.get(accountNumber1)) {
//
}
}
}
}
这样我们就避免了循环等待。
我假设你的 Hashmap 写入是线程安全的或者它是只读的。
无法获得多个对象的单个锁。但还有其他选择。
您可以使用共享(又名 "global")锁。问题是这个锁可能是一个瓶颈。
您可以使用其他某个对象作为这两个对象的代理,并获得对其的锁定。例如,假设您的帐户具有唯一 ID:
String lockName = (acc1.getAccountNumber() + "-" + acc2.getAccountNumber()).intern(); synchronized (lockName) { // we now have a lock on the combination of acc1 and acc2. }
(但是,这可能不适用于您的用例,因为它不会停止涉及两个帐户之一和第三个帐户的同时转账。)
按规范顺序获取锁。例如。
if (acc1.getAccountNumber().compareTo(acc2.getAccountNumber()) < 0) { synchronized (acc1) { synchronized (acc2) { // do transfer } } else { synchronized (acc2) { synchronized (acc1) { // do transfer } } }
如果所有线程以相同的顺序获取锁,则不会发生死锁。
使用
Lock.tryLock
获取锁并加一次。然而,这有几个问题:- 您现在需要管理每个帐户
Lock
个对象。 - 存在"livelock"的(理论)问题。要解决此问题,您可以使用随机生成的超时值。
- 您现在需要管理每个帐户
(注意:不要尝试使用身份哈希码作为唯一 id 的代理。身份哈希码不是唯一的,而且您还会遇到内存中可能有多个的问题 Account
表示相同逻辑帐户的对象...如果它们是 DTO。)