Java 并发编程实战
王宝令
资深架构师
72485 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
学习攻略 (1讲)
Java 并发编程实战
15
15
1.0x
00:00/00:00
登录|注册

04 | 互斥锁(下):如何用一把锁保护多个资源?

转账
转账
余额
查看密码
更改密码
查看余额
取款
账户密码
锁:保护账户密码
账户余额
锁:保护账户余额
class Account
this锁无法保护别人的余额
class Account
细粒度锁
性能问题
class Account
示例代码
使用Account.class作为共享的锁
唯一性的对象
并发转账问题
问题出现
转账操作示例
用一把锁的问题
示例代码
账户余额和账户密码用this作为互斥锁的可行性
关联关系与原子性
梳理访问路径
选择合适的锁
分析多个资源之间的关系
共享锁的方案
有关联关系的多个资源
没有关联关系的多个资源
课后思考
总结
使用锁的正确姿势
保护多个资源
互斥锁

该思维导图由 AI 生成,仅供参考

在上一篇文章中,我们提到受保护资源和锁之间合理的关联关系应该是 N:1 的关系,也就是说可以用一把锁来保护多个资源,但是不能用多把锁来保护一个资源,并且结合文中示例,我们也重点强调了“不能用多把锁来保护一个资源”这个问题。而至于如何保护多个资源,我们今天就来聊聊。
当我们要保护多个资源时,首先要区分这些资源是否存在关联关系。

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

在现实世界里,球场的座位和电影院的座位就是没有关联关系的,这种场景非常容易解决,那就是球赛有球赛的门票,电影院有电影院的门票,各自管理各自的。
同样这对应到编程领域,也很容易解决。例如,银行业务中有针对账户余额(余额是一种资源)的取款操作,也有针对账户密码(密码也是一种资源)的更改操作,我们可以为账户余额和账户密码分配不同的锁来解决并发问题,这个还是很简单的。
相关的示例代码如下,账户类 Account 有两个成员变量,分别是账户余额 balance 和账户密码 password。取款 withdraw() 和查看余额 getBalance() 操作会访问账户余额 balance,我们创建一个 final 对象 balLock 作为锁(类比球赛门票);而更改密码 updatePassword() 和查看密码 getPassword() 操作会修改账户密码 password,我们创建一个 final 对象 pwLock 作为锁(类比电影票)。不同的资源用不同的锁保护,各自管各自的,很简单。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了使用互斥锁来保护多个资源的技术细节。首先,介绍了保护无关联资源的方法,如在银行业务中对账户余额和密码进行并发保护。其次,讨论了细粒度锁的概念,以提升性能。然后,探讨了保护有关联资源的挑战,指出使用同一把锁保护多个相关资源可能导致并发问题。最后,提出了使用类级别的锁来保护不同对象的临界区,解决多个相关资源的并发访问问题。总的来说,本文对互斥锁在多资源保护中的应用技巧进行了深入浅出的介绍,对于理解并发编程中的锁机制具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(180)

  • 最新
  • 精选
  • 少主江衫
    用this.balance 和this.password 都不行。在同一个账户多线程访问时候,A线程取款进行this.balance-=amt;时候此时this.balance对应的值已经发生变换,线程B再次取款时拿到的balance对应的值并不是A线程中的,也就是说不能把可变的对象当成一把锁。this.password 虽然说是String修饰但也会改变,所以也不行。老师所讲的例子中的两个Object无论多次访问过程中都未发生变化? 请老师指正。

    作者回复: 正确,不能用可变对象做锁

    2019-03-07
    8
    222
  • 树森
    有个疑问,使用Account.class获得锁,那所有转账操作不是都成串行了,这里实践中可行吗?

    作者回复: 不可行,下一期讲优化

    2019-03-07
    8
    177
  • 老杨同志
    思考题: 我觉得不能用balance和password做为锁对象。这两个对象balance是Integer,password是String都是不可变变对象,一但对他们进行赋值就会变成新的对象,加的锁就失效了

    作者回复: 是的

    2019-03-07
    8
    83
  • 夜空中最亮的星
    我是一名普通的运维工程师,我是真看不懂java代码,我是来听思想的 。

    作者回复: 那我就放心了

    2019-03-07
    9
    81
  • 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-09
    10
    60
  • 别皱眉
    老师,很感谢有这个专栏,让我能够更加系统的学习并发知识。 对于思考题,之所以不可行是因为每次修改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-13
    2
    45
  • wang
    不可以。因为balance为integer对象,当值被修改相当于换锁,还有integer有缓存-128到127,相当于同一个对象。

    作者回复: 深刻!👍

    2019-03-07
    35
  • zhaozp
    可变对象不能作为锁

    作者回复: 总结的到位

    2019-03-07
    22
  • 0bug
    思考题: 结论:不可行 原因:举个例子,假如this.balance = 10 ,多个线程同时竞争同一把锁this.balance,此时只有一个线程拿到了锁,其他线程等待,拿到锁的线程进行this.balance -= 1操作,this.balance = 9。 该线程释放锁, 之前等待锁的线程继续竞争this.balance=10的锁,新加入的线程竞争this.balance=9的锁,导致多个锁对应一个资源

    作者回复: 分析的很仔细👍

    2019-03-07
    3
    20
  • 强哥
    文章里第二个例子根本无法用到实践中,锁力度太大,可以用乐观关锁解决,另外分布式的情况下,应该如何分析也应该讲讲?至于原子性其实跟数据库的原子性还是有差异的,例如虚拟机异常退出时,synchinzed也无法操作原子操作的。

    作者回复: 分布式的不讲了,分支太多不好。下一期会讲优化

    2019-03-07
    2
    13
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部