中间件核心技术与实战
丁威
中通快递资深架构师,RocketMQ 社区首席布道师
19674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
中间件核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

06 | 锁:如何理解锁的同步阻塞队列与条件队列?

你好,我是丁威。
这节课,我们重点介绍并发编程中的基石:锁。

锁的基本存储结构

我们先通过一个简单的场景来感受一下锁的使用场景。一家三口在一起生活,家里只有一个卫生间,大家早上起床之后都要去厕所。这时候,一个人在卫生间,其他人就必须排队等待。
这个场景用 IT 术语可以表述为下面两点。
洗手间作为一个资源在同一时间只能被一个人使用,它具备排他性。
一个人用完洗手间(资源)之后会归还锁,然后排队者重新开始竞争洗手间的使用权。
我们可以对这个场景进行建模。
资源:更准确地说是公共资源或共享资源需要被不同的操作者使用,但它不能同时被使用。
资源使用者:共享资源的使用者。
锁:用来保护资源的访问权。锁对象的归属权为共享资源,但当资源使用者向资源申请操作时,可以将锁授予资源使用者。这时候,资源使用者叫做锁的占有者,在此期间它有权操作资源。操作者不再需要操作资源之后,主动将锁归还。
排队队列:我们可以更专业地称之为阻塞队列,它可以存储需要访问资源但还没获取锁的资源使用者,其归属权通常为锁对象。
这里我之所以强调归属权,主要是因为它可以帮助我们理解锁的基本结构和资源的关系。
那锁的结构是什么呢?我们通过上节课的课后题来理解这个问题。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了并发编程中的关键概念——锁的原理和底层实现机制。通过生动的比喻和具体的代码示例,读者可以快速了解锁的排他性和资源保护概念,以及锁的基本存储结构和底层实现原理。文章详细介绍了AQS(AbstractQueuedSynchronizer)的类图和各个类的含义,以及ReentrantLock对标synchronized的实现原理。重点解析了锁的申请过程,包括tryLock方法和tryAcquireNanos方法的实现细节,以及CAS(比较-交换)机制在锁申请中的重要作用。此外,文章还对AQS的内部数据结构和方法进行了深入解析,帮助读者更加深入地理解锁的本质和实现原理。总体而言,本文通过生动的比喻和具体的代码示例,将抽象的并发编程概念具体化,使读者能够快速了解锁的同步阻塞队列与条件队列的概念及其底层实现机制。文章内容涵盖了锁的释放、条件等待队列等关键概念,为读者提供了全面的并发编程知识。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《中间件核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(9)

  • 最新
  • 精选
  • 末日,成欢
    对于AQS框架的理解, 我的感觉它更像是synchronized的JAVA实现。 在进入synchronized代码内部的时候, 是通过monitorenter指令, 修改了对象头的Markword. AQS在进入的时候, 通过CAS修改了state变量。 释放的时候同理。 当多个线程阻塞排队的时候, synchonized同样存在入口的等待队列EntryList,和AQS类似。 当使用wait/notifyAll的时候, synchronized同样存在条件队列waiSet, 也和AQS的条件队列类似。 持有线程ownerThread,也是类似的。 AQS又是对synchronized的功能的增强。 比如增加了非阻塞式锁,支持打断式获取锁、支持超时式获取锁。 并且大师们更对AQS实现了公平锁、非公平锁,还有一些更好用的工具类。 ReentrantReadWriteLock也是使用AQS实现的。 它对state的高低16位有更多的功能。 读用的是高16位,写用的是低16位 并且读锁使用的是共享模式, 也就是当写锁释放时, 会将读线程一个一个唤醒。

    作者回复: 嗯,对的,非常不错,我当时就认真看了大师写的AQS,才真正对锁有了正确的认知,正确认知锁后,对多线程并发编程就能得心应手。

    2022-07-23归属地:上海
    8
  • 码小呆
    感觉一遍还不够我大脑理解,需要多看看!

    作者回复: 你好,在看的过程中,如果有什么疑问,可以找我细聊

    2022-06-26
    1
  • :)
    老师,你好, 看了下源码ReentrantLock 是 implements Lock 而不是 extends ,为什么还用内部类Sync,而不是直接extends AbstractQueuedSynchronizer?

    作者回复: 你好,你这个问题问的非常好。我发表一下我的一些看法,欢迎探讨执教。 这里其实涉及到设计模式的一些实践。 AbstractQueuedSynchronizer 这个类,主要用来定义一些锁的共同方法,锁的一些模版方法,但锁的类型有读写锁、公平锁,公平锁、信号量、CountDownlatch,这些都有一些共性方法,但他们之间又很多差异,如果继承一个基类,由于存在较大差异,代码会很乱,并且受限于java单继承机制,所以就采用来内部类,来解决多重继承,并且引入两个维度。

    2022-06-25归属地:上海
    2
    1
  • 小豹哥
    这也太仔细认真了吧,这细节抠的! 给老师点个大大的赞

    作者回复: 谢谢认可,之所以我在这花这么大的篇幅在介绍juc中锁的实现,是因为我从一个毫无高并发编程经验的菜鸟,慢慢通过研读juc这块的代码,从而对多线程编程有了实打实的在原理层面的开窍,就是有那种悟道、豁然开朗的感觉。

    2022-06-24归属地:上海
    1
  • TableBear
    老师,请问你的源码是JDK什么版本的?

    作者回复: 你好,使用的版本是jdk1.8的

    2022-07-06
    2
  • 雨落~紫竹
    我又来了

    作者回复: 谢谢你的互动,期待我们更多的技术碰撞,一起进步。

    2022-07-02
  • smilekaka
    太干了,有点噎住....
    2023-07-26归属地:江苏
  • 小麦
    “如果节点的状态为 Node.CONDITION,或者 node.prev 为空,表示线程在等待条件被触发。为什么节点的前驱节点不为空就可以认为线程在同步阻塞队列中呢?这是因为进入同步队列时是用 CAS 机制来更新前驱节点的。” AQS 貌似没有通过 CAS 更新前驱节点的操作。 private static final long stateOffset; private static final long headOffset; private static final long tailOffset; private static final long waitStatusOffset; private static final long nextOffset; 没看到有前驱节点的偏移量。
    2022-08-04归属地:广东
  • Zx
    有一个小问题请教老师,为什么唤醒节点要从队尾开始找最靠前的非取消节点,而不是从队头开始找呢
    2022-06-27
    1
收起评论
显示
设置
留言
9
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部