多线程与高并发(五):AQS

一、简介

1、什么是AQS

AQS全名AbstractQueuedSynchronizer,抽象的队列式的同步器,它定义了一套多线程访问共享资源的同步器框架,许多同步类都依赖于它,如ReentrantLock、CountDownLatch、Semaphore等

2、AQS的结构

 AQS主要为一个volatile的state和一个CLH队列组成。


二、AQS原理

AQS核心思想是,如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中。

1、state

AQS主要通过state去控制线程对共享资源的访问,此处的state用volatile修饰,主要原因为让state在线程间可见。

不同的同步器继承AQS后,通过对部分方法的重写,对state值进行不同的处理,以实现对共享资源的不同竞争方式。竞争方式一般分为两种:独占(ReentrantLock)和共享(CountDownLatch、Semaphore)

1.1、state的访问方式有三种:

getState:获取state当前值

setState:主要用于初始化时使用

compareAndSetState:主要用于对state进行更新时使用

1.2、需要子类重写的主要方法:

① isHeldExclusively:(在Condition中使用)当前线程是否正在独占资源。Returns true if synchronization is held exclusively with respect to the current (calling)

protected boolean isHeldExclusively() {
	throw new UnsupportedOperationException();
}

② tryAcquire:独占方式。尝试获取资源,成功则返回true,失败则返回false。

protected boolean tryAcquire(int arg) {
	throw new UnsupportedOperationException();
}

③ tryRelease:独占方式。尝试释放资源,成功则返回true,失败则返回false。

protected boolean tryRelease(int arg) {
	throw new UnsupportedOperationException();
}

④ tryAcquireShared:共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

protected int tryAcquireShared(int arg) {
	throw new UnsupportedOperationException();
}

⑤ tryReleaseShared:共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

protected boolean tryReleaseShared(int arg) {
	throw new UnsupportedOperationException();
}

注:在AQS中已经实现了这些方法,在设计模式中称为“模板方法”,但它们会直接抛出异常,目的是让子类选择自己需要用的方法进行重写,而不必重写所有方法。

 

2、CLH队列

CLH是一个带有自旋锁的双向队列,能确保无饥饿性,通过FIFO实现线程对共享资源的访问。

2.1、Node节点:

2.1.1、节点中的属性:

Node是CLH中的节点,它维护了以下两个主要属性:

(1)节点状态waitStatus:

① CANCELLED(1):表示当前结点已取消调度。当timeout或被中断(响应中断的情况下),会触发变更为此状态,进入该状态后的结点将不会再变化。

② SIGNAL(-1):表示后继结点在等待当前结点唤醒。后继结点入队时,会将前继结点的状态更新为SIGNAL。

③ CONDITION(-2):表示结点等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

④ PROPAGATE(-3):当前线程处在SHARED情况下,该字段才会使用,共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。

⑤ 0:新结点的初始化状态。

注:

① 非负值表示该节点处于被取消状态,大多数代码不需要检查特定的值,只用判断符号即可。

② 对于普通节点,waitStatus一般初始化为0

③ 对于condition节点,waitStatus一般初始化为CONDITION(-2)

(2)thread:

节点中维护的线程

2.1.1、节点的两种模式:

① SHARED:共享模式

② EXCLUSIVE:独占模式

 

3、获取共享资源流程:

注:

tryAcquire为模板方法,需要子类进行重写。

若子类为公平锁,tryAcquire只会在共享资源空闲或当前线程为资源独占线程时,才会成功,否则会失败。

若子类为非公平锁,tryAcquire除了在共享资源空闲或当前线程为资源独占线程时,还会去尝试CAS修改state的值,若修改成功,则获取成功,否则失败。

 

4、释放资源

① 尝试释放资源

② 唤醒后继节点

 

三、AQS的应用

AQS主要通过state去控制线程对共享资源的访问,不同的同步器继承AQS后,通过对部分方法的重写,对state值进行不同的处理,以实现对共享资源的不同竞争方式。

1、ReentrantLock

当state=0时,代表资源处于空闲状态,线程可去竞争资源。

当state>0时,代表资源已经被某个线程占有,此时state的值由当前线程进行修改值,state的数值为该线程重入的次数,每重入一次state+1,每释放一次state-1,当state减到0时,资源完全释放。

2、CountDownLatch

state的值为CountDownLatch的计数器,当state>0时,调用countDownLatch.await的线程等待,每当有一个线程调用countDownLatch.countDown,state-1,当state减到0时,等待的线程被唤醒。

3、Semaphore

state的值表示剩余的信号量,每当有线程调用semaphore.acquire获取到信号量时,state-1,每当拿到信号量的线程调用semaphore.release时state+1。当state=0时,表示无剩余信号量,此时正在调用semaphore.acquire的线程等待,当有其它线程释放信号量时,会唤醒clh队列中下一个等待的线程去争抢信号量。

 

参考资料:https://www.cnblogs.com/waterystone/p/4920797.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值