AbstractQueuedSynchronizer同步器是实现JUC核心基础组件,因为 定义了一套多线程访问共享资源的同步器框架。前面几篇文章中JUC同步工具中都利用AQS构建自身的阻塞类。AQS解决了子类实现同步器时涉及的大量细节问题,例如获取同步状态、FIFO同步队列等;同时采用模板方法模式,AQS实现大量通用方法,子类通过继承方式实现其抽象方法来管理同步状态。先看看其UML图:

     

  首先从类名看这是一个抽象队列同步器,那么是这样的队列呢?CLH同步队列。

     

  AQS它维护了一个 volatile int state(代表共享资源)和一个 FIFO 线程等待队列(多线程争用资源被阻塞时会进入此队列)。

  通过AQS UML图可知state有三种访问方式:

    

  线程等待队列节点详细结构源码如下:

    

  其中acquire/release、acquireShared/releaseShared 是AQS里面的两对模板方法。以acquire为例跟踪分析具体方法:

    

  获取操作可能是独占的exclusive(ReentrantLock),也可能是非独占的share(Semaphore和CountDownLatch),取决于不同的Synchronizer。

    

  AQS 只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现, AQS 这里只定义了一个接口,具体资源的获取交由自定义同步器去实现了(通过 state 的 get/set/CAS)之所以没有定义成abstract ,是 因 为独 占模 式 下 只 用实现 tryAcquire-tryRelease ,而 共享 模 式 下 只用 实 现tryAcquireShared-tryReleaseShared。如果都定义成abstract,那么每个模式也要去实现另一模式下的接口。不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等), AQS 已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:        

    1. isHeldExclusively():该线程是否正在独占资源。只有用到 condition 才需要去实现它。

    2. tryAcquire(int):独占方式。尝试获取资源,成功则返回 true,失败则返回 false。

    3. tryRelease(int):独占方式。尝试释放资源,成功则返回 true,失败则返回 false。

    4. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败; 0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

    5. tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回 false。

  一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现 tryAcquiretryRelease、 tryAcquireShared-tryReleaseShared 中的一种即可。 但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如 ReentrantReadWriteLock。

  一个获取操作分为两步:

    第一步,Synchronizer判断当期状态是否允许被获得。如果是,就让线程执行;如果不是,获取操作阻塞或者失败。具体判断有Synchronizer的语意决定,可以举例说明:如果想成功获取锁,锁必须是未被占有的;二如果想成功地获取闭锁,闭锁必须未处于终止状态。

    第二步包括了可能需要更新的状态。一个想获取Synchronizer的线程会影响到其他线程是否能够获取它。例如,获取锁的操作将锁的状态从“未被占有”改变为“已被占有”;从Semaphore中获取许可的操作会检索剩余许可的数量。另一方面,一个响应对闭锁的请求操作却不会影响到其他薪酬是否能够获取他,所以获取闭锁的操作不会改变闭锁的状态。

  除以上提到的tryXXX方法需要在子类中重写,其他AQS提供的模板方法可参考显式锁之ReentrantLock实现 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)及相关利用AQS实现的阻塞类的使用。

  

  

    

    

Java并发基础之AbstractQueuedSynchronizer(AQS)的更多相关文章

  1. Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

    AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...

  2. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  3. Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  4. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  5. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  6. java并发基础(五)--- 线程池的使用

    第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...

  7. java并发基础(二)

    <java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...

  8. Java并发基础概念

    Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...

  9. java并发基础及原理

    java并发基础知识导图   一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...

随机推荐

  1. iOS二进制方案真实落地经验(30分钟降低到10分钟以内)

    iOS二进制方案真实落地经验(30分钟降低到10分钟以内) 我们做iOS二进制化断断续续尝试了一年多了,来来回回换了三个架构师去尝试落地,今日完全落地,在此做个总结 背景 工程基于cocoapod的组 ...

  2. Go 中实现用户的每日限额(比如一天只能领三次福利)

    如果你写一个 bug 管理系统,用了这个 PeriodLimit 你就可以限制每个测试人员每天只能给你提一个 bug.工作是不是就轻松很多了? 如今微服务架构大行其道本质原因是因为要降低系统的整体复杂 ...

  3. MySQL注入点写webshell的五种方式

    0x00 写数据的前提 1,在mysql的配置文件 my.ini 中,secure_file_priv="c:/wamp64/tmp" 被注释 或者 `secure_file_pr ...

  4. redis的bind误区

    对于Redis中bind的正确的理解是:bind:是绑定本机的IP地址,(准确的是:本机的网卡对应的IP地址,每一个网卡都有一个IP地址),而不是redis允许来自其他计算机的IP地址.如果指定了bi ...

  5. HTML、CSS、Javascript、jQuery、Xml

    HTML HTML简介 Hyper Text Markup Language (超文本标记语言)简写:HTML.通过标签来标记要显示的网页中的各个部分.网页文件本身是一种文本文件,通过在文本文件中添加 ...

  6. 《剑指offer》面试题68 - II. 二叉树的最近公共祖先

    问题描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q ...

  7. 使用PostGIS完成两点间的河流轨迹及流经长度的计算

    基础准备工作 1.PostGIS 的安装 在安装PostGIS前首先必须安装PostgreSQL,然后再安装好的Stack Builder中选择安装PostGIS组件.具体安装步骤可参照 PostGI ...

  8. Ioc容器-Bean管理(工厂bean)

    IoC操作Bean管理(FactoryBean) 1,Spring有两种类型bean,一种像自己创建的普通bean,另一种工厂bean(FactoryBean) 2,普通bean:在spring配置文 ...

  9. 集合框架-工具类-Collections-折半最值

    1 package cn.itcast.p2.toolclass.collections.demo; 2 3 import java.util.ArrayList; 4 import java.uti ...

  10. Java 实现订单未支付超时自动取消

    在电商上购买商品后,如果在下单而又没有支付的情况下,一般提示30分钟完成支付,否则订单自动.比如在京东下单为完成支付: 超过24小时,就会自动取消订单,下面使用 Java 定时器实现超时取消订单功能. ...