| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 聊聊AbstractQueuedSynchronizer -> 正文阅读 |
|
[Java知识库]聊聊AbstractQueuedSynchronizer |
文章目录什么是AbstractQueuedSynchronizerAbstractQueuedSynchronizer是一个框架,一个基于FIFO队列用于实现阻塞锁和同步器的框架。 AbstractQueuedSynchronizer类设计为能支持大多数情况的同步器,其中通过一个原子性的value来表示锁的状态(state),对于实现者来说,只需要安全的去改变这个state(什么叫安全),然后定义state处于什么情况(什么值)下是获取(acquired),释放(released)。 基于此同步器需要在内部创建一个helper来继承于AbstractQueuedSynchronizer,然后在定义自己的一些同步方法,在同步方法中按照自己的逻辑去访问helper方法以达到同步目的。(范例)。 AbstractQueuedSynchronizer提供了两种模式,一种独占模式(exclusive),一种共享模式(shared),通过独占表示只有一个线程能占有,共享代表着允许多个线程同时持有。 实现同步器步骤要实现AQS只需要按照自己的需要完成下面几个方法的实现就可以了:
对于获取锁的场景会有两种:
安全的去改变stateAbstractQueuedSynchronizer中定义了getState(),setState(),compareAndSetState()三个方法来操作state,基于此在自己实现上述的五个方法,可以参考范例 中的tryAcquir(),tryRelease()等方法的实现。 范例
AbstractQueuedSynchronizer设计通过上面描述我们简单的感受了下AbstractQueuedSynchronizer是做什么的,以及我们该怎么做,那么接下来我们就将详细看看AbstractQueuedSynchronizer中是如何设计整个框架的。 第一步:给框架进行定位AbstractQueuedSynchronizer的定位是一个同步器框架,是一个基于FIFO队列用于实现阻塞锁和同步器的框架。 基于此,我们就需要准备一些东西:
对于第一点,既然是锁既然是同步器,那么我们就必须要有实际的物体来锁定,而不能是虚无的,它可以是对象,可以是任意一个实实在在的东西。AbstractQueuedSynchronizer选择int类型来做为资源标识,也就是上述的state成员变量。 对于第二点,AbstractQueuedSynchronizer选择CLH队列的变种,一个节点持有前后对象的双向链表来作为阻塞队列。 根据上面的结构和资源,一个简单的流程就有了: 第二步:抽象需自定义的API对于争夺来说,不同的场景会存在不一样的需求,对于ReentrantLock可重入锁同一个线程允许多次进入,但是在范例中实现的锁就只能进入一次。 因此,需要将争夺相关的操作抽象出来,让实现方去自定义,同理争夺进行了自定义,那么释放必然就存在则差别,同样需要进行自定义。 而锁存在则两种模式,一种独占一种共享,对于框架来说,是没办法直接明白本次争夺是独占还是共享,那么只能进行机械式的拆分为两个方法来表示这层含义。 也就诞生出了这五种需要实现方关注的方法:
第三步:CLH变种队列的设计与管理现在我们明确了资源,队列结构,争夺实现。接下来就需要完成队列的管理。
队列结构基于CLH变种队列,在AbstractQueuedSynchronizer中将持有队列的头对象与尾对象来帮助完成快速进入队列和出队列。
对于头部head,只能通过setHead()方法进行修改,对于尾部tail只能通过enq()方法添加到队列中。 队列成员属性通过队列结构我们知道队列中的成员类型都为Node类型。
基于CLH变种的双向链表,必然存在前节点和后节点,对应的成员就是prev和next。 节点成员有几种状态节点对应的操作通过waitStatus成员来进行表示
节点状态机
创建节点时
节点进入队列后,将前驱节点变更为SIGNAL
doReleaseShare进行释放
doReleaseShared进行释放
0
初始化
SIGNAL
PROPAGATE
共享模式队列操作通过上面对队列结构的分析和节点node的描述,接下来就该看下队列的操作中是如何使用到队列结构与节点的。 共享模式-总体思路原则:
由第一个节点的waitStatus状态(SIGNAL)来判断是否进行后继节点的唤醒(出队),唤醒时机在释放(releaseShared)是进行。 第一种简单情形: node3进入队列前:
node1:SIGNAL
node2:0
node3进入队列后:
node1:SIGNAL
node2:SIGNAL
node3:0
第二种包含CANCELLED节点
node1:SIGNAL
node2:CANCELLED
node3进入队列后:
node1:SIGNAL
node3:0
共享模式-出入队
addWaiter()
setHeadAndPropagate()
doReleaseShared()1.要么将后继节点唤醒,把自身状态重置为0
PROPAGATE状态产生的原因起因是由于共享锁,同时进行释放导致有资源的情况下线程需要持续等待。 在没有PROPAGATE的情况下 当node1进行释放时,唤醒node3: node1释放,state+1。 当node3准备将自己设置为头部之前,node2进行释放: 场景描述清楚了,那么解决方案就是新增一个PROPAGATE状态,将0变更为PROPAGATE状态表示在进行头部节点的替换过程中存在资源的释放,同时在setHeadAndPropagate()判断是否唤醒后续节点的条件加上,当head处于PROPAGATE状态时。 共享模式队列操作跟共享差别不大,获取锁时不在有扩散行为(PROPAGATE),不再做分析。 ConditionObject条件队列后续在聊 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 6:01:25- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |