互斥锁(下)(如何用一把锁保护多个资源())
1)当我们要保护多个资源时,首先要做的是什么?
- 分析这些资源是否存在关联关系
- 应该怎样保护没有关联关系的多个资源?**就是没有关联关系的,这种场景非常容易解决,那就是球赛有球赛的门票,电影院有电影院的门票,各自管理各自的
- 对应到编程领域,例如,银行业务中有针对账户余额(余额是一种资源)的取款操作,也有针对账户密码(密码也是一种资源)的更改操作,我们可以为账户余额和账户密码分配不同的锁来解决并发问题
- 管密码的是一把锁,管余额的是另外一把锁,各管各的,井水不犯河水。
?
class Account {
// 锁:保护账户余额
private final Object balLock
= new Object();
// 账户余额
private Integer balance;
// 锁:保护账户密码
private final Object pwLock
= new Object();
// 账户密码
private String password;
?
// 取款
void withdraw(Integer amt) {
synchronized(balLock) {
if (this.balance > amt){
this.balance -= amt;
}
}
}
// 查看余额
Integer getBalance() {
synchronized(balLock) {
return balance;
}
}
?
// 更改密码
void updatePassword(String pw){
synchronized(pwLock) {
this.password = pw;
}
}
// 查看密码
String getPassword() {
synchronized(pwLock) {
return password;
}
}
}
3.1)上面的代码中我们可以用一把锁来保护密码和余额这两个资源吗?
- 可以,在所有方法上加synchronized关键字,锁都是this。
- 性能太差,会导致取款、查看余额、修改密码、查看密码这四个操作都是串行的
- 用两把锁,取款和修改密码是可以并行的。
- 不同的锁对受保护资源进行精细化管理,能够提升性能
- 细粒度锁
- 银行业务里面的转账操作,账户 A 减少 100 元,账户 B 增加 100 元。这两个账户就是有关联关系的
?
class Account {
private int balance;
// 转账
void transfer(
Account target, int amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
4.1)怎么保证转账操作 transfer() 没有并发问题呢?用 synchronized 关键字修饰一下 transfer() 方法就可以了吗?
?
class Account {
private int balance;
// 转账
synchronized void transfer(
Account target, int amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
两个资源,一把this锁,这看上去完全正确的啊,有什么问题?
4.2)上面代码中的问题出现在哪里?
- 问题就出在 this 这把锁上。this 这把锁可以保护自己的余额 this.balance,却保护不了别人的余额 target.balance。
- 你不能用自家的锁来保护别人家的资产,也不能用自己的票来保护别人的座位一样。
文章图片
4.4)this只能保护自己,不能保护他人的实例?
A、B、C 三个账户,余额都是 200 元。
两个线程分别执行两个转账操作
- A 转给账户 B 100 元
- B 转给账户 C 100 元
假设线程 1 执行账户 A 转账户 B 的操作,线程 2 执行账户 B 转账户 C 的操作。
4.4.1)这两个线程分别在两颗 CPU 上同时执行,那它们是互斥的吗?
- 不是。线程 1 锁定的是账户 A 的实例(A.this),线程 2 锁定的是账户 B 的实例(B.this)。锁的对象不一样,他们两可以同时进入临界区,然后把B=200的值都读到自己的线程资源里面。
- 100 :线程1先执行,线程2后执行然后覆盖掉线程1的B值。
- 300:线程2先执行,线程1后执行然后覆盖掉线程1的B值。
- 反正就是不可能是正确的200!
文章图片
4.5)那么该怎样正确的去使用一把锁避免两个关联资源没有保护周全的情况?
- 用一把范围大的锁
4.6)如何让 A 对象和 B 对象共享一把锁呢?
- 让所有对象都持有一个唯一性的对象,这个对象在创建 Account 时传入。
?
class Account {
private Object lock;
private int balance;
private Account();
// 创建Account时传入同一个lock对象
public Account(Object lock) {
this.lock = lock;
}
// 转账
void transfer(Account target, int amt){
// 此处检查所有对象共享的锁
synchronized(lock) {
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
它要求在创建 Account 对象的时候必须传入同一个对象,如果不是同一个那就完蛋了。
- 用 Account.class 作为共享的锁(选择这种方案)
?
class Account {
private int balance;
// 转账
void transfer(Account target, int amt){
synchronized(Account.class) {
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
文章图片
- 【互斥锁(下)(如何用一把锁保护多个资源())】不能用balance和password做为锁对象。这两个对象balance是Integer,password是String都是可变对象,一但对他们进行赋值就会变成新的对象,加的锁就失效了。
推荐阅读
- 记录一下自己学习递归的过程(未完)
- 24位腾讯云专家精彩演讲,4万字《腾讯云技术实践精选集|24位腾讯云专家精彩演讲,4万字《腾讯云技术实践精选集 2021》发布!(附合集下载)
- 24位腾讯云专家精彩演讲,4万字《腾讯云技术实践精选集 2021》发布!(附合集下载)
- 数睿数据深度 | 商业智能红海,下一代BI还能激起多大的浪花
- Redisson分布式锁的实现原理及源码
- Vue下拉选择框Select组件使用详解(一)
- Vue下拉选择框Select组件使用详解(二)
- 一起来学习一下python的数据类型
- 一文概述(从状态复用到Hooks)
- Unity3D 2021.1.2F1 发布了。赋国际版本下载地址。