ReentrantLock - 并发汇款操作
ReentrantLock - Concurrent money transfer operation
当我在网上阅读一些并发代码示例时,我发现了这个(2 个银行账户之间的转账操作):
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 字段同步或锁定)以使该示例起作用?其他线程不可能潜入并调用 withdraw 或 deposit 方法吗?另外,如果有 getBalance() 方法怎么办?它是否也应该受到保护(与 ReentrantLock 同步或锁定)?
有两个选项:
(1) 你让你的 class 线程安全意味着在这个 class 的任何实例上的任何操作都受到某种内部机制的保护,并且在多线程环境中是绝对安全的。调用方不应该关心线程安全。
这就是我在这里更喜欢的。作为您 API 的消费者,我认为 Account#withdraw
和 Account#deposit
都是自给自足的,因此不需要额外的操作。
在我看来,好的 API 就是这样。
(2) 您将提供正确性和线程安全性的责任放在调用方。你不关心它是如何实现的。
这就是您的代码段当前的工作方式。 transfer
方法是线程安全的,但它不会使帐户操作如此。
shouldn't the Account's withdraw() and deposit() methods be somehow
protected
实际上,当下面的行执行时,代码块由 Lock
对象保护(每个 Account 对象都有自己的 Lock
对象)。因此,没有其他线程可以使用相同的 Account
实例执行相同的代码。
while(true)
{
if(from.lock.tryLock()){
try {
if (to.lock.tryLock()){
try{
....
....
另一方面,当您执行代码时,您正在创建多个 Account
对象,这使得每个传输彼此独立。因为,每个 Account
对象都有自己的状态 (balance
, lock
)
Also, what if there's a getBalance() method? Should it be protected
too
同上
当我在网上阅读一些并发代码示例时,我发现了这个(2 个银行账户之间的转账操作):
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 字段同步或锁定)以使该示例起作用?其他线程不可能潜入并调用 withdraw 或 deposit 方法吗?另外,如果有 getBalance() 方法怎么办?它是否也应该受到保护(与 ReentrantLock 同步或锁定)?
有两个选项:
(1) 你让你的 class 线程安全意味着在这个 class 的任何实例上的任何操作都受到某种内部机制的保护,并且在多线程环境中是绝对安全的。调用方不应该关心线程安全。
这就是我在这里更喜欢的。作为您 API 的消费者,我认为 Account#withdraw
和 Account#deposit
都是自给自足的,因此不需要额外的操作。
在我看来,好的 API 就是这样。
(2) 您将提供正确性和线程安全性的责任放在调用方。你不关心它是如何实现的。
这就是您的代码段当前的工作方式。 transfer
方法是线程安全的,但它不会使帐户操作如此。
shouldn't the Account's withdraw() and deposit() methods be somehow protected
实际上,当下面的行执行时,代码块由 Lock
对象保护(每个 Account 对象都有自己的 Lock
对象)。因此,没有其他线程可以使用相同的 Account
实例执行相同的代码。
while(true)
{
if(from.lock.tryLock()){
try {
if (to.lock.tryLock()){
try{
....
....
另一方面,当您执行代码时,您正在创建多个 Account
对象,这使得每个传输彼此独立。因为,每个 Account
对象都有自己的状态 (balance
, lock
)
Also, what if there's a getBalance() method? Should it be protected too
同上