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 就是根据 ...
随机推荐
- 纯C 实现 strpos substr strspilt str_trim
在C 语言中没有C++ 好用的 spilt 方法 (STL 带的也不怎么好用) #include <stdio.h> #include <string.h> #include ...
- Redis主从原理及哨兵模式
1.Redis主从搭建 主从的搭建很简单,主节点设置连接密码,从节点的配置上主节点的ip和端口,以及密码,一般从节点我们都设置只读模式. 主节点配置: 主节点密码: requirepass xxx 从 ...
- C语言程序设计(二) C数据类型
第二章 C数据类型 八进制整数由数字0开头,后跟0~7的数字序列组成. 十六进制整数由数字0加字母x(或X)开头,后跟0~9,a~f(或A~F)的数字序列组成. 整型常量: 默认的int型定义为有符号 ...
- python基础学习day4
列表的初识 why:int bool str str: 存储少量的数据. str:切片还是对其进行任何操作,获取的内容全都是str类型.存储的数据单一. what:list list = [66, ' ...
- java第一次上机练习作业
1.已知a,b均是整型变量,写出将a,b两个变量中的值互换的程序.(知识点:变量和 运算符综合应用) int a = 5, b = 10; int temp; temp = a; a = b; b = ...
- 利用EPX Studio将C/S程序转成B/S的方法详解(在线模块方式)
采用 EPX 的在线模块,是最简单的方法,包括实现简单,客户端不需任何设置,客户使用就简单. 1. 设置服务器端参数(EPServer) 1.1 在服务配置工具选项卡中,设置服务项中的名称,路径,激活 ...
- PyQt5UI文件转换为对应版本的py文件
PyQt5 UI文件转换为对应版本的py文件 #coding=utf-8 ''' PyQt5 UI文件转换为对应版本的py文件 python -m PyQt5.uic.pyuic untitled.u ...
- EOS基础全家桶(一)开篇
简介 从今天开始我会在FishoPark上与大家分享EOS的一些技术经验和基础,如果大家在看文章的过程中有任何问题,欢迎在网站下方的评论里留言,我会尽力为大家解答,如果发现我内容中所写有错,欢迎指正, ...
- 微商贴吧企业新浪微博怎么找客户_怎么做好seo
写的几篇文章对于seo的引流量方面的 怎么做好网络seo优化之SEO项目团队组建 品牌营销_中小企业网络推广的办法 微商怎么引流客源?微商没有客户的解决办法?下 微商怎么引流客源?微商没有客户的解决办 ...
- Swift 4.0 字符串(String)学习
定义字符串常量(常量只有读操作) let lString = "constant" let lString1: String = "constant" 定义字符 ...