并发编程之互斥锁

并发编程之互斥锁

经典场景之转账

声明账户类:Account,该类有一个成员变量余额:balance,还有一个用于转账的方法:transfer()。

class Account {
  //余额
  private int balance;
  
  // 给目标账户转账
  void transfer(Account target, int amt){
    if (this.balance > amt) {
      //自己余额做减法
      this.balance -= amt;
      //目标账户余额做加法
      target.balance += amt;
    }
  } 
}

假设有三个账户:账户A、账户B、账户C,三个账户里面的余额都是200。同一时刻,账户A 向 账户B 转账100,账户B 向 账户C 转账100,正确结果应该是 账户A 余额为100,账户B 余额为200, 账户C 余额为 300。然后怎么保证转账操作 transfer() 没有并发问题呢 ?

方法1,用 synchronized 关键字修饰一下 transfer() 方法,如下:

 class Account {
  private int balance;
  
  synchronized void transfer(
      Account target, int amt){
    if (this.balance > amt) {
      this.balance -= amt;
      target.balance += amt;
    }
  } 
}

分析:synchronized放在普通方法上用的锁是:this。

线程1(A给B转账):this 对象是 账户A;
线程2(B给C转账):this 对象是 账户B;
线程1 与 线程2 并不互斥,因为要拿的是不同的锁,所以可以发生在同一时刻。

结果:
账户 B 的余额可能是 300(线程 1 后于线程 2 写 B.balance,线程 2 写的 B.balance 值被线程 1 覆盖),也可能是 100(线程 1 先于线程 2 写 B.balance,线程 1 写的 B.balance 值被线程 2 覆盖),就是不可能是 200。

总结: 该方法 不能 保证转账操作 transfer()是并发安全的。

方法2

 class Account {
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    synchronized(Account.class) {
      if (this.balance > amt) {
        this.balance -= amt;
        target.balance += amt;
      }
    }
  } 
}

分析:synchronized锁的是全局唯一的:Account.class。

线程1(A给B转账):锁对象是 Account.class;
线程2(B给C转账):锁对象是 Account.class;
线程1 与 线程2 互斥,因为要拿的是相同的锁,所以不可以发生在同一时刻。

总结: 该方法 可以 保证转账操作 transfer()是并发安全的。但是,所有的账户转账操作都使用的同一个锁:Account.class。那么所有的转账操作就会变成串行的操作,所以该方法虽然安全,可是确实不可行的。

方法3

class Account {
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    // 锁定转出账户
    synchronized(this) {              
      // 锁定转入账户
      synchronized(target) {           
        if (this.balance > amt) {
          this.balance -= amt;
          target.balance += amt;
        }
      }
    }
  } 
}

分析:方法1是因为只锁住了 this 对象,线程1 和 线程2 要锁的对象不互斥导致线程不安全;方法2是因为用一个全局的锁,导致所有账户的转账操作变成了串行。所以方法3,使用两把锁,锁住当前账户对象 和 目标对象。

线程1(A给B转账):1)锁住 账户A,2)锁住 账户B;
线程2(B给C转账):1)锁住 账户B,2)锁住 账户A;
线程1 与 线程2 互斥,因为不可能都同时获取到账户A和账户B的锁,所以不可以发生在同一时刻。

总结:该方法可以保证并发安全。但是,如果当 线程1 获取到 账户A 的锁(等待线程2释放 账户B 的锁),线程2 获取到 账户B 的锁(等待线程2释放 账户B 的锁)。两个线程就陷入了死锁状态。

所以,未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值