摘要:当你使用java实现一个线程同步的对象时 , 一定会包含一个问题:你该如何保证多个线程访问该对象时 , 正确地进行阻塞等待 , 正确地被唤醒?本文分享自华为云社区《JUC中的AQS底层详细超详解 , 剖析AQS设计中所需要考虑的各种问题!》 , 作者: breakDawn。
java中AQS究竟是做什么的?当你使用java实现一个线程同步的对象时 , 一定会包含一个问题:
你该如何保证多个线程访问该对象时 , 正确地进行阻塞等待 , 正确地被唤醒?
关于这个问题 , java的设计者认为应该是一套通用的机制
因此将一套线程阻塞等待以及被唤醒时锁分配的机制称之为AQS
全称 AbstractQuenedSynchronizer
中文名即抽象的队列式同步器。
基于AQS , 实现了例如ReentenLock之类的经典JUC类 。
AQS简要步骤
- 线程访问资源 , 如果资源足够 , 则把线程封装成一个Node , 设置为活跃线程进入CLH队列 , 并扣去资源
- 资源不足 , 则变成等待线程Node , 也进入CLH队列
- CLH是一个双向链式队列, head节点是实际占用锁的线程 , 后面的节点则都是等待线程所对应对应的节点
private volatile int state;protected final int getState() { return state;}protected final void setState(int newState) {state = newState;}// cas方法compareAndSetState(int oldState, int newState);如果state上限只有1 , 那么就是独占模式Exclusive , 例如 ReentrantLock
如果state上限大于1 , 那就是共享模式Share , 例如 Semaphore、CountDownLatch、ReadWriteLock , CyclicBarrier
已经有CAS方法了 , 为什么资源state还要定义成volatile的?对外暴露的getter/setter方法 , 是走不了CAS的 。而且setter/getter没有被synchronized修饰 。所以必须要volatile , 保证可见性
这样基于AQS的实现可以直接通过getter/setter操作state变量 , 并且保证可见性 , 也避免重排序带来的影响 。比如CountDownLatch , ReentrantReadWriteLock , Semaphore都有体现(各种getState、setState)
对资源的操作什么时候用CAS , 什么使用setState?volatile的state成员有一个问题 , 就是如果是复合操作的话不能保证复合操作的原子性
因此涉及 state增减的情况 , 采用CAS
如果是state设置成某个固定值 , 则使用setState
AQS的CLH队列为什么需要一个CLH队列这个队列的目的是为了公平锁的实现
即为了保证先到先得 , 要求每个线程封装后的Node按顺序拼接起来 。
CLH本质?是一个Queue容器吗不是的 , 本质上是一个链表式的队列
因此核心在于链表节点Node的定义
文章插图
除了比较容易想到的prev和next指针外
还包含了该节点内的线程
以及 waitStatus 等待状态
4种等待状态如下:
- CANCELLED(1): 因为超时或者中断 , 节点会被设置为取消状态 , 被取消的节点时不会参与到竞争中的 , 他会一直保持取消状态不会转变为其他状态;
- SIGNAL(-1):后继节点的线程处于等待状态 , 而当前节点的线程如果释放了同步状态或者被取消 , 将会通知后继节点 , 使后继节点的线程得以运行
经验总结扩展阅读
- 许运东婉萍是什么电视剧中的人物?
- 为什么女儿9岁了睡觉头上出汗
- 迎战高考的励志祝福文案
- 许舒贝是什么电视剧中的人物?
- 裘正宇是什么电视剧中的人物?
- 西厂雨化田是什么电影中的人物?
- Briefings in Bioinformatics-2021 知识图谱-生物信息学-医学顶刊论文:生物信息学中的图表示学习:趋势、方法和应用
- 苏简和黄凯迪是什么电视剧中的人物?
- 3步训练培养你心目中的小神童
- 艾莉是什么电视剧中的人物?