没有合适的资源?快使用搜索试试~ 我知道了~
深入理解Java中的AQS.docx
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 46 浏览量
2022-06-27
22:12:08
上传
评论
收藏 353KB DOCX 举报
温馨提示
试读
23页
AQS是一个通过内置的 FIFO 双向队列来完成线程的排队工作(内部通过结点head和tail记录队首和队尾元素,元素的结点类型为Node类型,后面我们会看到Node的具体构造)。 /*等待队列的队首结点(懒加载,这里体现为竞争失败的情况下,加入同步队列的线程执行到enq方法的时候会创 建一个Head结点)。该结点只能被setHead方法修改。并且结点的waitStatus不能为CANCELLED*/ private transient volatile Node head; /**等待队列的尾节点,也是懒加载的。(enq方法)。只在加入新的阻塞结点的情况下修改*/ private transient volatile Node tail;
资源推荐
资源详情
资源评论
深入理解 Java 中的 AQS
AQS 概述
AbstractQueuedSynchronizer 抽象队列同步器简称 AQS,它是实现同步器的基
础组件,juc 下面 Lock 的实现以及一些并发工具类就是通过 AQS 来实现的,这
里我们通过 AQS 的类图先看一下大概,下面我们总结一下 AQS 的实现原理。
先看看 AQS 的类图。
(1)AQS 是一个通过内置的FIFO双向队列来完成线程的排队工作(内部通过结
点 head 和 tail 记录队首和队尾元素,元素的结点类型为 Node 类型,后面我们
会看到 Node 的具体构造)。
/*等待队列的队首结点(懒加载,这里体现为竞争失败的情况下,加入同步队列的线程执行到 enq
方法的时候会创
建一个 Head 结点)。该结点只能被 setHead 方法修改。并且结点的 waitStatus 不能为
CANCELLED*/
private transient volatile Node head;
/**等待队列的尾节点,也是懒加载的。(enq 方法)。只在加入新的阻塞结点的情况下修改*/
private transient volatile Node tail;
(2)其中Node中的 thread 用来存放进入 AQS 队列中的线程引用,Node 结点
内部的 SHARED 表示标记线程是因为获取共享资源失败被阻塞添加到队列中的;
Node 中的 EXCLUSIVE 表示线程因为获取独占资源失败被阻塞添加到队列中的。
waitStatus 表示当前线程的等待状态:
CANCELLED=1① :表示线程因为中断或者等待超时,需要从等待队列中取消
等待;
SIGNAL=-1② :当前线程 thread1 占有锁,队列中的 head(仅仅代表头结点,里
面没有存放线程引用)的后继结点 node1 处于等待状态,如果已占有锁的线程
thread1 释放锁或被 CANCEL 之后就会通知这个结点 node1 去获取锁执行。
CONDITION=-2③ :表示结点在等待队列中(这里指的是等待在某个 lock 的
condition 上,关于 Condition 的原理下面会写到),当持有锁的线程调用了
Condition 的 signal()方法之后,结点会从该 condition 的等待队列转移到该 lock
的同步队列上,去竞争 lock。(注意:这里的同步队列就是我们说的 AQS 维护
的 FIFO 队列,等待队列则是每个 condition 关联的队列)
PROPAGTE=-3④ :表示下一次共享状态获取将会传递给后继结点获取这个共
享同步状态。
(3)AQS 中维持了一个单一的 volatile 修饰的状态信息 state(AQS 通过 Unsafe
的相关方法,以原子性的方式由线程去获取这个 state)。AQS 提供了
getState()、setState()、compareAndSetState()函数修改值(实际上调用的是
unsafe 的 compareAndSwapInt 方法)。下面是 AQS 中的部分成员变量以及更
新 state 的方法
//这就是我们刚刚说到的 head 结点,懒加载的(只有竞争失败需要构建同步队列的时候,才会创
建这个 head),如果头节点存在,它的 waitStatus 不能为 CANCELLED
private transient volatile Node head;
//当前同步队列尾节点的引用,也是懒加载的,只有调用 enq 方法的时候会添加一个新的 wait
node
private transient volatile Node tail;
//AQS 核心:同步状态
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
(4)AQS 的设计师基于模板方法模式的。使用时候需要继承同步器并重写指
定的方法,并且通常将子类推荐为定义同步组件的静态内部类,子类重写这些
方法之后,AQS 工作时使用的是提供的模板方法,在这些模板方法中调用子类
重写的方法。其中子类可以重写的方法:
//独占式的获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进
行 CAS 设置同步状态
protected boolean tryAcquire(int arg) { throw new
UnsupportedOperationException();}
//独占式的释放同步状态,等待获取同步状态的线程可以有机会获取同步状态
protected boolean tryRelease(int arg) { throw new
UnsupportedOperationException();}
//共享式的获取同步状态
protected int tryAcquireShared(int arg) { throw new
UnsupportedOperationException();}
//尝试将状态设置为以共享模式释放同步状态。 该方法总是由执行释放的线程调用。
protected int tryReleaseShared(int arg) { throw new
UnsupportedOperationException(); }
//当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占
protected int isHeldExclusively(int arg) { throw new
UnsupportedOperationException();}
(5)AQS 的内部类ConditionObject是通过结合锁实现线程同步,
ConditionObject 可以直接访问 AQS 的变量(state、queue),ConditionObject
是个条件变量 ,每个 ConditionObject 对应一个队列用来存放线程调用
condition 条件变量的 await 方法之后被阻塞的线程。
AQS 中的独占模式
上面我们简单了解了一下 AQS 的基本组成,这里通过ReentrantLock的非
公平锁实现来具体分析 AQS 的独占模式的加锁和释放锁的过程。
非公平锁的加锁流程
简单说来,AQS 会把所有的请求线程构成一个 CLH 队列,当一个线程执行完
毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列
中,而那些等待执行的线程全部处于阻塞状态(park())。如下图所示。
(1)假设这个时候在初始情况下,还没有多任务来请求竞争这个 state,这时候
如果第一个线程 thread1 调用了 lock 方法请求获得锁,首先会通过 CAS 的方式
将 state 更新为 1,表示自己 thread1 获得了锁,并将独占锁的线程持有者设置
为 thread1。
final void lock() {
if (compareAndSetState(0, 1))
//setExclusiveOwnerThread 是 AbstractOwnableSynchronizer 的方法,AQS
继承了 AbstractOwnableSynchronizer
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
(2)这个时候有另一个线程 thread2 来尝试或者锁,同样也调用 lock 方法,尝
试通过 CAS 的方式将 state 更新为 1,但是由于之前已经有线程持有了 state,
所以 thread2 这一步 CAS 失败(前面的 thread1 已经获取 state 并且没有释放),
就会调用 acquire(1)方法(该方法是 AQS 提供的模板方法,它会调用子类的
tryAcquire 方法)。非公平锁的实现中,AQS 的模板方法 acquire(1)就会调用
NofairSync 的 tryAcquire 方法,而 tryAcquire 方法又调用的 Sync 的
nonfairTryAcquire 方法,所以我们看看 nonfairTryAcquire 的流程。
//NofairSync
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
//(1)获取当前线程
final Thread current = Thread.currentThread();
//(2)获得当前同步状态 state
int c = getState();
//(3)如果 state==0,表示没有线程获取
if (c == 0) {
//(3-1)那么就尝试以 CAS 的方式更新 state 的值
if (compareAndSetState(0, acquires)) {
剩余22页未读,继续阅读
资源评论
小兔子平安
- 粉丝: 210
- 资源: 1940
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功