【问题标题】:ReentrantLock - Concurrent money transfer operationReentrantLock - 并发转账操作
【发布时间】:2018-12-25 12:20:14
【问题描述】:

当我在互联网上阅读一些并发代码示例时,我发现了这个(两个银行账户之间的汇款操作):

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 同步或锁定)?

【问题讨论】:

  • 这两段代码是否都属于Account 类?
  • 恕我直言,这段代码的和平只是为了展示如何使transfer方法线程安全。

标签: java multithreading concurrency reentrantlock


【解决方案1】:

有两种选择:

(1) 你让你的类线程安全意味着对这个类的任何实例的任何操作都受到某种内部机制的保护,并且在多线程环境中是绝对安全的。调用方不应该关心线程安全。

这就是我想要的。作为 API 的使用者,除了 Account#withdrawAccount#deposit 之外,我会自给自足,因此不需要额外的操作。

这就是我认为好的 API 的样子。

(2) 您将提供正确性和线程安全的责任放在调用方。你不在乎它是如何实现的。

这就是您的 sn-ps 当前的工作方式。 transfer 方法是线程安全的,但它不会进行账户操作。

【讨论】:

  • 所以要遵循方法 1,Account 类中的方法withdraw() 和 deposit() 应该包含它们对 ReentrantLock 实例的锁定/解锁调用,对吧?这将使消费者免于处理线程问题。对吗?
【解决方案2】:

账户的withdraw() 和deposit() 方法不应该是某种方式吗? 受保护

实际上,当下面的行执行时,代码块被Lock 对象保护(并且每个Account 对象都有自己的Lock 对象)。因此,没有其他线程可以使用相同的 Account 实例执行相同的代码。

while(true)
    {
      if(from.lock.tryLock()){
        try { 
            if (to.lock.tryLock()){
               try{
          ....
          ....

另一方面,当您执行代码时,您会创建多个Account 对象,这使得每个传输彼此独立。因为,每个Account 对象都有自己的状态(balancelock

另外,如果有 getBalance() 方法呢?是否应该受到保护 太

如上。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 2015-05-29
    • 1970-01-01
    • 2017-11-08
    • 2022-08-08
    相关资源
    最近更新 更多