上节课作者卖了个关子,就是如何用同一把锁保护多个资源。
一
作者表示,可以用一把互斥锁来保护多个资源,例如用 this 这一把锁来管理账户类里所有的资源:账户余额和用户密码。
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;
}
}
}
具体表现为,将示例程序中所有的方法都增加同步关键字 synchronized 即可。
但作者认为,用一把锁有个问题,就是性能太差,会导致取款、查看余额、修改密码、查看密码这四个操作都是串行的。
而用两把锁,取款和修改密码是可以并行的。
这样用不同的锁对受保护资源进行精细化管理,能够提升性能(这种锁也叫细粒度锁)。
所以,作者认为不同的资源最好用不同的锁来保护。
二
再看一段代码
class Account {
private int balance;
// 转账
synchronized void transfer(
Account target, int amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
怎么保证转账操作 transfer() 没有并发问题呢?
作者表示,在这段代码中,临界区内有两个资源,分别是转出账户的余额 this.balance 和转入账户的余额 target.balance 并且用的是同一把锁 this 这把锁可以保护自己的余额 this.balance 却保护不了别人的余额 target.balance(详见用锁 this 保护 this.balance 和 target.balance 的示意图)。
三
再如,假设有 A、B、C 三个账户,余额都是 200 元,用两个线程分别执行两个转账操作:账户 A 转给账户 B 100 元,账户 B 转给账户 C 100 元,最后期望的结果应该是账户 A 的余额是 100 元,账户 B 的余额是 200 元,账户 C 的余额是 300 元(详见分析过程与并发转账示意图)。
四
作者表示,只要锁能覆盖所有受保护资源就可以了。示例代码如下
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;
}
}
}
}
更好的方案如下
class Account {
private int balance;
// 转账
void transfer(Account target, int amt){
synchronized(Account.class) {
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
}
五
作者认为,如何保护多个资源,关键是要分析多个资源之间的关系。
如果资源之间没有关系,很好处理,每个资源一把锁就可以了。
如果资源之间有关联关系,就要选择一个粒度更大的锁,这个锁应该能够覆盖所有相关的资源。
除此之外,还要梳理出有哪些访问路径,所有的访问路径都要设置合适的锁。