04 | 互斥锁(下):如何用一把锁保护多个资源?
王宝令
该思维导图由 AI 生成,仅供参考
在上一篇文章中,我们提到受保护资源和锁之间合理的关联关系应该是 N:1 的关系,也就是说可以用一把锁来保护多个资源,但是不能用多把锁来保护一个资源,并且结合文中示例,我们也重点强调了“不能用多把锁来保护一个资源”这个问题。而至于如何保护多个资源,我们今天就来聊聊。
当我们要保护多个资源时,首先要区分这些资源是否存在关联关系。
保护没有关联关系的多个资源
在现实世界里,球场的座位和电影院的座位就是没有关联关系的,这种场景非常容易解决,那就是球赛有球赛的门票,电影院有电影院的门票,各自管理各自的。
同样这对应到编程领域,也很容易解决。例如,银行业务中有针对账户余额(余额是一种资源)的取款操作,也有针对账户密码(密码也是一种资源)的更改操作,我们可以为账户余额和账户密码分配不同的锁来解决并发问题,这个还是很简单的。
相关的示例代码如下,账户类 Account 有两个成员变量,分别是账户余额 balance 和账户密码 password。取款 withdraw() 和查看余额 getBalance() 操作会访问账户余额 balance,我们创建一个 final 对象 balLock 作为锁(类比球赛门票);而更改密码 updatePassword() 和查看密码 getPassword() 操作会修改账户密码 password,我们创建一个 final 对象 pwLock 作为锁(类比电影票)。不同的资源用不同的锁保护,各自管各自的,很简单。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了使用互斥锁来保护多个资源的技术细节。首先,介绍了保护无关联资源的方法,如在银行业务中对账户余额和密码进行并发保护。其次,讨论了细粒度锁的概念,以提升性能。然后,探讨了保护有关联资源的挑战,指出使用同一把锁保护多个相关资源可能导致并发问题。最后,提出了使用类级别的锁来保护不同对象的临界区,解决多个相关资源的并发访问问题。总的来说,本文对互斥锁在多资源保护中的应用技巧进行了深入浅出的介绍,对于理解并发编程中的锁机制具有一定的参考价值。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》,新⼈⾸单¥59
《Java 并发编程实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(180)
- 最新
- 精选
- 少主江衫用this.balance 和this.password 都不行。在同一个账户多线程访问时候,A线程取款进行this.balance-=amt;时候此时this.balance对应的值已经发生变换,线程B再次取款时拿到的balance对应的值并不是A线程中的,也就是说不能把可变的对象当成一把锁。this.password 虽然说是String修饰但也会改变,所以也不行。老师所讲的例子中的两个Object无论多次访问过程中都未发生变化? 请老师指正。
作者回复: 正确,不能用可变对象做锁
2019-03-078222 - 树森有个疑问,使用Account.class获得锁,那所有转账操作不是都成串行了,这里实践中可行吗?
作者回复: 不可行,下一期讲优化
2019-03-078177 - 老杨同志思考题: 我觉得不能用balance和password做为锁对象。这两个对象balance是Integer,password是String都是不可变变对象,一但对他们进行赋值就会变成新的对象,加的锁就失效了
作者回复: 是的
2019-03-07883 - 夜空中最亮的星我是一名普通的运维工程师,我是真看不懂java代码,我是来听思想的 。
作者回复: 那我就放心了
2019-03-07981 - yuc是否可以在Account中添加一个静态object,通过锁这个object来实现一个锁保护多个资源,如下: class Account { private static Object lock = new Object(); private int balance; // 转账 void transfer(Account target, int amt){ synchronized(lock) { if (this.balance > amt) { this.balance -= amt; target.balance += amt; } } } }
作者回复: 这种方式比锁class更安全,因为这个缺是私有的。有些最佳实践要求必须这样做。👍
2019-03-091060 - 别皱眉老师,很感谢有这个专栏,让我能够更加系统的学习并发知识。 对于思考题,之所以不可行是因为每次修改balance和password时都会使锁发生变化。 ----------------------------------------------------------------------- 以下只是我的猜想 比如有线程A、B、C 线程A首先拿到balance1锁,线程B这个时候也过来,发现锁被拿走了,线程B被放入一个地方进行等待。 当A修改掉变量balance的值后,锁由balance1变为balance2. 线程B也拿到那个balance1锁,这时候刚好有线程C过来,拿到了balance2锁。 由于B和C持有的锁不同,所以可以同时执行这个方法来修改balance的值,这个时候就有可能是线程B修改的值会覆盖掉线程C修改的值? ----------------------------------------------------------------------- 不知道到底是不是这样?老师可以详细讲下这个过程吗?谢谢
作者回复: 你分析的很仔细了,就是这样的,bc锁的不是一个对象。不能保证互斥性
2019-03-13245 - wang不可以。因为balance为integer对象,当值被修改相当于换锁,还有integer有缓存-128到127,相当于同一个对象。
作者回复: 深刻!👍
2019-03-0735 - zhaozp可变对象不能作为锁
作者回复: 总结的到位
2019-03-0722 - 0bug思考题: 结论:不可行 原因:举个例子,假如this.balance = 10 ,多个线程同时竞争同一把锁this.balance,此时只有一个线程拿到了锁,其他线程等待,拿到锁的线程进行this.balance -= 1操作,this.balance = 9。 该线程释放锁, 之前等待锁的线程继续竞争this.balance=10的锁,新加入的线程竞争this.balance=9的锁,导致多个锁对应一个资源
作者回复: 分析的很仔细👍
2019-03-07320 - 强哥文章里第二个例子根本无法用到实践中,锁力度太大,可以用乐观关锁解决,另外分布式的情况下,应该如何分析也应该讲讲?至于原子性其实跟数据库的原子性还是有差异的,例如虚拟机异常退出时,synchinzed也无法操作原子操作的。
作者回复: 分布式的不讲了,分支太多不好。下一期会讲优化
2019-03-07213
收起评论