java - ReentrantLock - 并发转账操作
问题描述
当我在互联网上阅读一些并发代码示例时,我发现了这个(两个银行账户之间的汇款操作):
class Account {
double balance;
int id;
public Account(int id, double balance){
this.balance = balance;
this.id = id;
}
void withdraw(double amount){
balance -= amount;
}
void deposit(double amount){
balance += amount;
}
}
class Main{
public static void main(String [] args){
final Account a = new Account(1,1000);
final Account b = new Account(2,300);
Thread a = new Thread(){
public void run(){
transfer(a,b,200);
}
};
Thread b = new Thread(){
public void run(){
transfer(b,a,300);
}
};
a.start();
b.start();
}
这段代码使用 ReentrantLock 处理并发问题:
private final Lock lock = new ReentrantLock(); //Addition to the Account class
public static void transfer(Account from, Account to, double amount)
{
while(true)
{
if(from.lock.tryLock()){
try {
if (to.lock.tryLock()){
try{
from.withdraw(amount);
to.deposit(amount);
break;
}
finally {
to.lock.unlock();
}
}
}
finally {
from.lock.unlock();
}
Thread.sleep(someRandomTimeToPreventLiveLock);
}
}
我的问题是:Acount 的withdraw() 和deposit() 方法是否应该以某种方式保护(与ReentrantLock 字段同步或锁定)以使该示例正常工作?其他线程是否有可能潜入并调用提款或存款方法?另外,如果有 getBalance() 方法怎么办?它是否也应该受到保护(与 ReentrantLock 同步或锁定)?
解决方案
有两种选择:
(1) 你让你的类线程安全意味着对这个类的任何实例的任何操作都受到某种内部机制的保护,并且在多线程环境中是绝对安全的。调用方不应该关心线程安全。
这就是我更喜欢这里。作为您的 API 的消费者,我除了两者之外,Account#withdraw
并且Account#deposit
要自给自足,因此不需要额外的操作。
这就是我认为好的 API 的样子。
(2) 您将提供正确性和线程安全的责任放在调用方。你不在乎它是如何实现的。
这就是您的代码段当前的工作方式。该方法transfer
是线程安全的,但它不会使帐户操作如此。
推荐阅读
- java - 为什么 List.contains(Object) 的行为不同?
- r - 如何在同一个数据帧上做colsum和average
- powerbi - 使用 DAX 生成日期系列
- javascript - 仅在服务器上需要一个包
- java - Intellij IDEA 仅针对所有未提交的更改运行测试
- regex - 在换行符之前匹配字符,不包括空格?
- macos - MacOS - 使用带有launchd的脚本 - 启动,登录,注销,关闭?
- service-worker - Workbox 的服务人员在更改时未更新
- python - AllenNLP 共指分辨率的多 GPU 训练
- reactjs - 事件处理程序中带有 [name] 的 PrevState