AbstractQueuedSynchronizer概述
AbstractQueuedSynchronizer 是一个用于在竞争资源(如多线程)时使用的同步器,它内部使用了一个int类型的字段status表示需要同步的资源状态, 并基于一个先进先出(FIFO)的等待队列,队列中的每个节点表示要获取资源的线程
工作流程
同步器主要是用于控制资源的获取以及释放,它可以用于独占模式和共享模式,这里我们以独占模式为例
在获取和释放资源时,我们需要实现自己的尝试获取和尝试释放的方法,利用
status字段来控制成功与否
获取资源
// 在独占模式下尝试获取资源
protected boolean tryAcquire(int arg) {
// 尝试获取资源与释放资源都是依靠 status 来实现具体的逻辑
// 可以基于 status 与 arg 字段来实现此方法 (我们可以赋予status任何意义来实现逻辑)
}
获取资源的源码如下(独占模式)
首先尝试获取资源,如果成功直接返回,进行后续流程
失败则创建一个新节点,将其添加到链表尾部(如果链表为空,则先创建一个空的表头再添加)
之后判断当前节点前一个节点是不是头结点,如果是则再次尝试获取资源,成功则将当前节点置为头节点,进行获取资源后的流程
如果当前节点前一个节点不是头结点,那么将当前节点中的线程阻塞(阻塞前务必将其前一节点状态改为signal),等待被唤醒
唤醒后跳转到第3步
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
释放资源
// 在独占模式下尝试释放资源
protected boolean tryRelease(int arg) {
// 尝试获取资源与释放资源都是依靠 status 来实现具体的逻辑
// 可以基于 status 与 arg 字段来实现此方法 (我们可以赋予status任何意义来实现逻辑)
}
释放资源的源码如下(独占模式) 1.首先尝试释放资源 2.成功后判断,如果头结点不为null,同时其状态不是初始0值(需要有后继节点更改其状态),那么将当前节点状态置为0,同时唤醒下一节点中线程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享模式与独占模式基本相同
区别主要在于此方法,当线程被唤醒后获取资源,如果成功且返回值>0,则会继续唤醒后续线程 返回负数:失败 返回0:成功,但是其他线程无法再获取资源 返回正数:成功,其他线程可能继续获取资源(需要尝试后知道)
protected int tryAcquireShared(int arg) {
// todo
}
下面举一个《Java并发编程实战》中的二元闭锁例子来说明AQS的使用
/**
* 使用 AQS 实现的二元闭锁(所有线程都会阻塞,直到状态改变被唤醒,此时所有线程都得到执行)
* status表是开关状态,=0时关闭,=1时开启
*/
public class OneShotLatch {
private final Sync sync = new Sync();
public void signal() {
sync.releaseShared(0);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(0);
}
private class Sync extends AbstractQueuedSynchronizer {
/**
* 如果闭锁是开的 (state==1),那么这个操作成功,否则失败阻塞
* @param ignored
* @return
*/
@Override
protected int tryAcquireShared(int ignored) {
return (getState() == 1) ? 1: -1;
}
/**
* 打开status状态开关,放开所有线程
* @param ignored
* @return
*/
@Override
protected boolean tryReleaseShared(int ignored) {
setState(1);
return true;
}
}
// 测试方法
public static void main(String[] args) throws Exception {
OneShotLatch oneShotLatch = new OneShotLatch();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
oneShotLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("completed");
}).start();
}
// 2S后唤醒
TimeUnit.SECONDS.sleep(2);
oneShotLatch.signal();
TimeUnit.SECONDS.sleep(5);
System.out.println("end");
}
}
AbstractQueuedSynchronizer概述的更多相关文章
- 30_AQS
[参考文章] https://www.jianshu.com/p/df0d7d6571de http://ifeve.com/introduce-abstractqueuedsynchronizer/ ...
- 万字长文!从底层开始带你了解并发编程,彻底帮你搞懂java锁!
线程是否要锁住同步资源 锁住 悲观锁 不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞 阻塞 不阻塞自旋锁,适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别 不锁住资源,多个线程只有一个能修改资 ...
- 并发编程 20—— AbstractQueuedSynchronizer 深入分析
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析
经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...
- Java并发包源码学习之AQS框架(一)概述
AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.con ...
- 4.锁--Synchronizer Framework Base Class—AbstractQueuedSynchronizer介绍
1. AQS简单介绍 AQS是Java并发类库的基础.其提供了一个基于FIFO队列,可以用于构建锁或者其它相关同步装置的基础框架.该同步器(下面简称同步器)利用了一个int来表示状态,期望它可以成为实 ...
- AbstractQueuedSynchronizer 原理分析 - 独占/共享模式
1.简介 AbstractQueuedSynchronizer (抽象队列同步器,以下简称 AQS)出现在 JDK 1.5 中,由大师 Doug Lea 所创作.AQS 是很多同步器的基础框架,比如 ...
- AbstractQueuedSynchronizer AQS框架源码剖析
一.引子 Java.util.concurrent包都是Doug Lea写的,来混个眼熟 是的,就是他,提出了JSR166(Java Specification RequestsJava 规范提案), ...
- 并发系列(4)之 AbstractQueuedSynchronizer 源码分析
本文将主要讲述 AbstractQueuedSynchronizer 的内部结构和实现逻辑,在看本文之前最好先了解一下 CLH 队列锁,AbstractQueuedSynchronizer 就是根据 ...
随机推荐
- ubunto 免输入密码 登录 putty ssh-keygen
交互式密码不安全,现在改用 ssh 证书方式,不用输入密码使用公钥证书登录. 方法1, 此方法,仅试用于,仅使用win putty 来连接方式使用,如果双方都是 linux 如 rsync 同步等时, ...
- java中的对象 方法 引用 等一些抽象的概念是什么意思呢?
2020-03-14 最近这一段时间有点忙,好久都没有更新博客了,之后我会一直坚持下去的,和大家一同进步的. 这段时间一直在学java,相信刚开始学习java的小白,刚开始接触那么些抽象的概念一定和我 ...
- django 从零开始 2迁移模型数据到数据库中和admin初识
和flask 一样 如果模型models 发生改动,则需要进行一个迁移数据库,但是我还没有想讲到那么深入,现在模型是django自带的一些sessiong,damin之类的模型 所以如果你想进去adm ...
- css 一行自适应等比例布局
一.浮动布局+百分比 .row { width:100%; overflow:hidden; zoom:1; } .item { float: left; width: 20%; } 该样式兼容性较好 ...
- C++ 文件操作 FILE*
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> //编程题:往文件里写入字母表的26个字母. //要求:如果字母对应编码值 是奇数则写 ...
- Windows10专业版+Microsoft office2016专业增强版免费无毒官方正版装机教程(简)
win10: 1.官网制作系统盘(具体见官网提示) 2.备份C盘 3.重启,主板调到USB优先(重启后疯狂按F12或del,具体看主板型号) 4.安装(这个看造化) 5.激活 slmgr /ipk N ...
- 一些http知识
一些http知识 前置知识 首先放一张非常经典的计算机网络 关于五层结构 「物理层」「数据链路层」「网络层」「运输层」「应用层」我们依次来讲 我们拿wireshark 抓个http包看一下就能明白各层 ...
- 《Python学习手册 第五版》 -第15章 文档
本章主要介绍Python中的文档,会通过多种方式来说明,如果查看Python自带文档和其他参考的资料 本章重点内容 1.#注释:源文件文档 2.dir函数:以列表显示对象中可用的属性 3.文档字符串 ...
- 优秀DevOps工程师必会的33个面试题
DevOps面试问题 01 您能告诉我们DevOps和Agile(敏捷)之间的根本区别吗? 答:尽管DevOps与敏捷方法(这是最流行的SDLC[Software Development Life C ...
- 01背包模板题 hdu2602 Bonecollector
由于数组的滚动过程中当前值(i,j)的更新需要用到上一层的(i-1,j-wi)的值,所以在更新当前的j之前不能更新上一层的j之前的值,故01背包是从后向前更新的(重量取值是从大到小的). 代码如下: ...