AbstractQueuedSynchronizer独占模式共享模式分析

AQS两种队列

同步等待队列

也称CLH队列,是一种基于双向链表数据结构的队列,是FIFO先进先出线程等待队列。
1、当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程。
2、当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

条件队列

条件队列是使用单向列表保存的
1、调用await方法会释放当前持有的锁,然后阻塞当前线程(进入条件队列)。
2、通过signal或signalAll将条件队列中的节点转移到同步队列。(由条件队列转到同步队列),并唤醒条件队列头节点。

独占模式

场景:使用ReentrantLock,Thread0获得锁,Thread1、Thread2未获取到锁入队阻塞

获取锁lock()

1、先尝试cas获取锁,若成功,则state=1,且exclusiveOwnerThread设为当前线程
2、acquire:若cas失败,则入队阻塞
2.1、tryAcquire:由子类实现,ReentrantLock通过cas尝试再获取一次锁
2.2、addWaiter(Node.EXCLUSIVE)入队:若队列为空,则需要构建初始队列;构建完成后采用尾插法,将节点插入队列尾部。
2.3、acquireQueued阻塞
for (;;)死循环判断
a、若该节点的前节点为头节点,则tryAcquire尝试获取一次资源,获取成功,则将该节点设置为头节点,并跳出循环。
b、shouldParkAfterFailedAcquire:将该节点的前节点的waitStatus设置为-1(SIGNAL)可唤醒状态。
c、parkAndCheckInterrupt:LockSupport.park(this)阻塞当前线程,当调用LockSupport.unPark(this)时则唤醒该线程,继续循环。

队列中节点变化

Thread1入队
在这里插入图片描述
Thread2入队
在这里插入图片描述

释放锁unlock()

release:释放锁
1、tryRelease:尝试释放锁,子类实现
ReentrantLock:将state减1,变为0;并将exclusiveOwnerThread设为null
2、unparkSuccessor:唤醒队列中线程
2.1、若头节点waitStatus != 0(此时队列中头节点为-1(SIGNAL));则开始唤醒后面阻塞的线程。
2.2、获取头节点的next节点,调用LockSupport.unpark进行唤醒(此时会激活获取锁流程中的2.3流程)。
2.3、独占锁只唤醒一个节点。

队列中节点变化

唤醒前
在这里插入图片描述
唤醒Thread1后
在这里插入图片描述

共享模式

场景:可用资源数为3,则Thread0~Thread2获得资源,Thread3、Thread4入队阻塞

获取资源

1、acquireSharedInterruptibly:共享模式下获取可用资源
2、tryAcquireShared:尝试获取一次锁,由子类具体实现
a、CountDownLatch:可用资源state为0则代表不需要阻塞,不为0则需要入队阻塞。
b、Semaphore:以公平模式为例,可用资源state>0,则不需要阻塞。
3、doAcquireSharedInterruptibly:入队阻塞
a、addWaiter(Node.SHARED)入队:若队列为空,则需要构建初始队列;构建完成后采用尾插法,将节点插入队列尾部。
b、for (;;)死循环判断
b1、若该节点的前节点为头节点,则tryAcquireShared尝试获取一次资源,获取成功,则将该节点设置为头节点,并跳出循环。
b2、shouldParkAfterFailedAcquire:将该节点的前节点的waitStatus设置为-1(SIGNAL)可唤醒状态。
b3、parkAndCheckInterrupt:LockSupport.park(this)阻塞当前线程,当调用LockSupport.unPark(this)时则唤醒该线程,继续循环。

队列中节点变化

Thread3入队
在这里插入图片描述
Thread4入队
在这里插入图片描述

释放资源

1、releaseShared:释放资源
1.1、tryReleaseShared:尝试释放资源,由子类实现
a、CountDownLatch:可以资源state-1,当state=0时,唤醒队列中的线程
b、Semaphore:可用资源state+1,并唤醒队列中的线程
1.2、doReleaseShared:
for (;;)死循环唤醒队列中的线程
a、获取头节点状态,若是waitStatus=Node.SIGNAL可唤醒状态,则开始唤醒队列中的线程。
b、unparkSuccessor:获取头节点的next节点,调用LockSupport.unpark进行唤醒(此时会激活获取资源流程中的3.b流程)。
c、继续唤醒队列中的后续节点,直到全部唤醒退出循环。

队列中节点的变化

唤醒前
在这里插入图片描述
Thread3唤醒
在这里插入图片描述
Thread4唤醒
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值