🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
**摘要:**当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒?
本文分享自华为云社区《JUC中的AQS底层详细超详解,剖析AQS设计中所需要考虑的各种问题!》,作者: breakDawn 。
java中AQS究竟是做什么的?
当你使用java实现一个线程同步的对象时,一定会包含一个问题:
你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒?
关于这个问题,java的设计者认为应该是一套通用的机制
因此将一套线程阻塞等待以及被唤醒时锁分配的机制称之为AQS
全称 AbstractQuenedSynchronizer
中文名即抽象的队列式同步器 。
基于AQS,实现了例如ReentenLock之类的经典JUC类。
AQS简要步骤
- 线程访问资源,如果资源足够,则把线程封装成一个Node,设置为活跃线程进入CLH队列,并扣去资源
- 资源不足,则变成等待线程Node,也进入CLH队列
- CLH是一个双向链式队列, head节点是实际占用锁的线程,后面的节点则都是等待线程所对应对应的节点
AQS的资源state
state定义
AQS中的资源是一个int值,而且是volatile的,并提供了3个方法给子类使用:
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):后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行
- CONDITION(-2) ÿ