浅析AQS

1、AbstractQueue抽象队列、BlockingQueue阻塞队列、Deque双端队列

2、Queue FIFO先进先出,

写入:队列满阻塞等待,取出:队列满阻塞等待生产

3、使用场景:多线程并发处理、线程池

4、 阻塞队列(BlockingQueue)——四组API(添加、移除、判断队列首部的场景)

==1、会抛出异常

==2、有返回值,不抛出异常

==3、阻塞等待(一直阻塞等待)

==4、超时等待

=========抛出异常 add()\remove()\element()=========

undefined

=========有返回值,不抛出异常 add()\remove()\element()=========

undefined

=========阻塞等待(一直阻塞等待) add()\remove()\element()=========

undefined

=========超时等待 add()\remove()\element()=========

undefined

5、 同步队列

==没有容量(即不长时间存储元素)

进去一个元素,必须等待取出(take())后才能再往里边继续添加(put())

==例子:

    BlockingQueue<String> blockingQueue = new SynchronousQueue<String>();

AQS

1、一个用来构建锁和同步器的框架,定义了锁的实现机制,并开放出扩展的地方,让子类去实现。(封装得很好,但又有子类扩展的地方)

例如:lock(加锁)时锁的内部机制:

使用锁Lock时,AQS开放state字段,然子类可以根据state字段来决定是否能够获得锁,对于获取不到锁的线程,AQS会自动进行管理,无需子类锁关心。

2、使用AQS能够简单且高效地构造出大量应用广泛的同步器:ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等,都基于AQS。也可以自定义同步器。

3、AQS底层:

==(1)由 同步队列 +条件队列 联合实现(CLH队列锁)

====一般情况下有同步队列(双向链表)组成,条件队列(单向链表)不是必须存在的,当程序中存在condition时,才会存在此列表。

====同步队列管理获取不到锁的线程的排队和释放

====条件队列是在一定场景下,对同步队列的补充(非必须的),如,获得锁的线程从空队列中拿数据(队列是空的,拿不到数据的),此时,条件队列会管理该线程,使线程阻塞。

==(2)核心思想:AQS内部维护一个CLH队列来管理。

====线程请求共享资源时,

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效线程,并且将共享资源设置为锁定状态;

如果被请求的共享资源被占用,则需要CLH队列锁实现的机制来实现线程阻塞等待以及线程被唤醒使锁的分配:即将暂时获取不到锁的线程加入到队列中。(上边的同步队列)

==(3)CLH锁队列:

====是一个虚拟的双向队列(不存在队列实例,仅存在节点间的关联关系)

====AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个个节点(Node)实现锁的分配。

==(4)AQS中同步队列的工作流程和数据结构:(结合两图理解)

浅析AQS

====工作过程:

(1)当前线程获取同步状态失败,同步器会将当线程及等待状态等信息构成一个Node节点,加入CLH队列中,放在队尾,同步器重新设置尾节点。

(2)加入队列后,会阻塞当前线程

(3)同步状态被释放并且同步器重新设置首节点,同步器唤醒等待队列中第一个节点,让其再次获取同步状态。

====AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。

====AQS使用CAS对该同步状态进行原子性操作实现对其值的修改

private volatile int state;

====状态信息通过protected类型的getState,setState,compareAndSetState进行操作

undefined

4、AQS对资源的共享方式

定义了两种资源共享方式:

==(1)Exclusive(独占):又分为公平锁和非公平锁

只有一个线程能执行,如ReentrantLock

====公平锁:按照线程在队列中的排队顺序, 先到者先拿到锁

====非公平锁:当前线程要获取锁时,无视队列内的顺序,直接去强锁,谁抢到了就是谁的。

==(2)Share(共享):

多个线程可以同时执行,如Semaphore\CyclicBarrier\ReadWriteLock\CountDownLatch

==(3)ReentrantReadWriteLock:组合两种资源共享方式的

====读锁运行多个线程同时执行对同一资源进行读操作

====写锁仅允许一个线程能执行对同一资源进行写操作

==(4)不同自定义的同步器有不同的共享资源方式,自定义同步器在实现时,只需要实现共享资源state的获取与释放方式即可。

5、AQS使用的设计模式

(1)基于模板设计模式的

(2)自定义同步器的方式:

==1、继承AbstractQueuedSynchronizer并重写指定的方法

重写方法是指对于共享资源state的获取和释放

==2、将AQS组合在自定义同步组件的实现中,并调用其模板方法

这些模板方法会调用上边继承重写的方法

====AQS提高的模板方法

protected boolean tryAcquire(int)

====除以上的方法外,AQS类中其它方法不能被重写,都为final

====例:

=======独占方式=======

ReentrantLock

(1)state初始化为0,表示未锁定状态;

(2)A线程lock()时,会调用tryAcquire()独占该锁并state+1;

(3)其它线程tryAcquire()时会失败,直到A线程unlock()到state=0,即释放,其它线程才能获取该锁。

(4)A线程释放前,A自己时可以重复获取此锁的(state++),即可重入

(5)A线程释放,一定要将state回归为state=0

=======共享方式=======

CountDownLatch,任务分为N个子线程进行执行

(1)state初始化为N(与线程数一致)

(2)N个子线程并行每执行countDown()一次,state CAS减一

(3)所有子线程都执行完成后即state=0,会unpark()主调用线程

(4)主调用线程从await()函数返回,继续后续的操作

6、AQS组件

(1)Semaphore(信号量):允许多个线程同时访问

====synchronized和ReentrantLock是一次只允许一个线程访问某个资源

(2)CountDownLatch(倒计时器):同步工具类,用来协调多个线程间的同步,通常用来控制线程等待,可以让某个线程等待直到倒计时结束,再开始执行。

(3)CyclicBarrier(循环栅栏):可以实现线程间的技术等待,功能更强大复杂。

== Cyclic(可循环使用)的Barrier(屏障):

====让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障(同步点)时,才会打开屏障,所有被拦截的线程此时才会继续执行。

====CyclicBarrier的默认构造方法:CyclicBarrier(int parties)

=======parties参数:表示屏障拦截的线程数量

=======每个线程调用await()方法告知CyclicBarrier已到达屏障,然后被阻塞。

Original: https://www.cnblogs.com/hexiayuliang666/p/16156939.html
Author: 与长安故里
Title: 浅析AQS

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570750/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • HTTP协议

    请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。 Accept Accept请求报头域用于指定客户端接受哪些类型的信息。eg:Accept:image/gif …

    Java 2023年6月7日
    092
  • 【Q&A】Fixing NuGet.targets(131,5) error The local source doesn’t exist

    当出现类似的nuget错误的时候 The local source ‘C:\Program Files (x86)\Microsoft Visual Studio\Shared\N…

    Java 2023年6月8日
    071
  • php实现关键词过滤

    在php中,可以通过trie_filter扩展实现关键词的过滤,具体操作如下 1.安装libdatrie的依赖库 libiconv 安装:libdatrie(需要最低版本为0.2….

    Java 2023年6月8日
    077
  • 每日一练

    2022/6/2 这种情况肯定不能修改 final int a=1; a=2; 那么引用类型呢? final int[] arr={1,2,3,4}; arr[1]=100; 可以…

    Java 2023年6月13日
    088
  • java线上故障排查

    线上故障主要会包括 CPU、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如 jstack、jmap 等工具也是…

    Java 2023年5月29日
    079
  • 关系数据库元数据处理类(一) 创建元数据实体

    1 /// 2 /// 数据库 3 /// 4 public class Database 5 { 6 public string Name { get; set; } 7 } 1…

    Java 2023年6月5日
    071
  • Java创建一个JDBC工具类并解决返回ResultSet的问题

    前段时间做数据源开发时用到了JDBC,碰到了一个问题,记录一下。 创建了一个JDBC工具类用来创建连接,并传入SQL查询,向调用者返回ResultSet对象,随后在工具类中关闭连接…

    Java 2023年6月9日
    0109
  • spring的事务管理

    点赞再看,养成习惯,微信搜索「 小大白日志」关注这个搬砖人。 文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。 事务的4种隔离级别,7种事务传播级别 Spring…

    Java 2023年6月8日
    094
  • MySQL七:一文详解六大日志

    转载~ 日志一般分为逻辑日志与物理日志两类 「逻辑日志」:即执行过的事务中的sql语句,执行的sql语句(增删改) 「反向」的信息 「物理日志」: mysql 数据最终是保存在数据…

    Java 2023年6月8日
    099
  • openjdk for window

    https://developers.redhat.com/products/openjdk/download/ https://github.com/dmlloyd/openjd…

    Java 2023年5月30日
    072
  • vue axios的二次封装

    1、axios的二次封装 BiliBili作者原地址,多多支持 npm i axios //下载axios 首先创建两个文件夹在src目录下;api和config 先在 confi…

    Java 2023年6月16日
    078
  • 【RocketMQ】读写队列

    一. 读写队列,是在路由时使用 在消息发送时,根据写队列个数返回路由信息,而消息消费时按照读队列个数返回路由信息。 二. 在物理文件层面,只有写队列才会创建文件 举个例子:写队列个…

    Java 2023年5月29日
    068
  • 关系数据库元数据处理类(一) 创建MSSQL元数据具体处理类

    1 public class SqlServer : BaseMetadata 2 { 3 4 public SqlServer(string connectionString) …

    Java 2023年6月5日
    055
  • 并发编程之:synchronized

    大家好,我是小黑,一个在互联网苟且偷生的农民工。 之前的文章中跟大家分享了关于Java中线程的一些概念和基本的使用方法,比如如何在Java中启动一个线程,生产者消费者模式等,以及如…

    Java 2023年6月7日
    0104
  • Maven 依赖调解源码解析(三):传递依赖,路径最近者优先

    本文是系列文章《Maven 源码解析:依赖调解是如何实现的?》第三篇,主要介绍依赖调解的第一条原则:传递依赖,路径最近者优先。 本篇内容较多,也是开始源码分析的第一篇,请务必仔细阅…

    Java 2023年6月16日
    075
  • arthas监控elasticsearch(7.x)

    arthas介绍 arthas是Alibaba推出的java诊断工具 官方文档 准备 准备docker环境 name port centos_arthas 3658:3658 do…

    Java 2023年6月8日
    091
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球