如何用一把锁保护多个资源?

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6


上一篇中提到受保护的资源和锁之间合理的关联关系应该是N:1的关系,也就是说可以用一把锁来保护多个资源,但是不能用多把锁来保护一个资源。

当我们要保护多个资源时,首先要区分这些资源是否存在关联关系。

保护没有关联关系的多个资源

我们可以用一把锁来保护多个资源,例如可以用this这一把锁来管理账户类里所有的资源。具体实现狠简单,就是给操作账户相关的方法都增加同步关键字synchronized就可以了。

但是用一把锁有个问题就是性能太差了,会导致操作账号的所有操作都串行执行,如果用不同的锁对受保护资源进行精细化管理,能够提升性能。

保护有关联关系的多个资源

举例,银行业务里的转账,账户A减少100,账户B增加100,这两个账户就是有关联关系的。用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.balance和传入账户的余额target.balance,并且用的是一把锁this,看上去似乎是正确的,但是是有问题的。问题出在this这把锁上,this这把锁可以保护自己的余额this.balance,但是保护不了别人的余额target.balance,就像你不能用自己家的锁保护别人家的资产一样。

使用锁的正确姿势

解决这个问题,只要我们的锁能够覆盖所有受保护资源就可以了,this是对象级别的锁,所以A对象和B对象都有自己的锁。

可以让所有的对象都持有一个唯一的对象,这个对象在创建Account时传入,把Account默认构造函数变为private,同时增加一个带有Object lock的参数的构造函数,创建对象时传入相同的lock,这样所有的Account对象就会共享这个lock了。

这样实现能解决问题,但是要求创建Account时传入的对象是同一个对象,如果创建时传入的lock不是同一个对象,就可能出现问题。

所以,还有更优解,就是用Account.class作为共享锁,Account.class是所有Account对象共享的,而且这个对象是JVM在加载Account类的时候创建的,不用担心唯一性。这种操作所有的转载操作都是串行了,在实际实践过程中一般是不可行的。还需要进一步优化。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6