AbstractQueuedSynchronizer 是一个用于在竞争资源(如多线程)时使用的同步器,它内部使用了一个int类型的字段status表示需要同步的资源状态, 并基于一个先进先出(FIFO)的等待队列,队列中的每个节点表示要获取资源的线程

工作流程

同步器主要是用于控制资源的获取以及释放,它可以用于独占模式和共享模式,这里我们以独占模式为例

在获取和释放资源时,我们需要实现自己的尝试获取和尝试释放的方法,利用status字段来控制成功与否

获取资源

// 在独占模式下尝试获取资源
protected boolean tryAcquire(int arg) {
   // 尝试获取资源与释放资源都是依靠 status 来实现具体的逻辑
   // 可以基于 status 与 arg 字段来实现此方法 (我们可以赋予status任何意义来实现逻辑)
}

获取资源的源码如下(独占模式)

  1. 首先尝试获取资源,如果成功直接返回,进行后续流程

  2. 失败则创建一个新节点,将其添加到链表尾部(如果链表为空,则先创建一个空的表头再添加)

  3. 之后判断当前节点前一个节点是不是头结点,如果是则再次尝试获取资源,成功则将当前节点置为头节点,进行获取资源后的流程

  4. 如果当前节点前一个节点不是头结点,那么将当前节点中的线程阻塞(阻塞前务必将其前一节点状态改为signal),等待被唤醒

  5. 唤醒后跳转到第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概述的更多相关文章

  1. 30_AQS

    [参考文章] https://www.jianshu.com/p/df0d7d6571de http://ifeve.com/introduce-abstractqueuedsynchronizer/ ...

  2. 万字长文!从底层开始带你了解并发编程,彻底帮你搞懂java锁!

    线程是否要锁住同步资源 锁住 悲观锁 不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞 阻塞 不阻塞自旋锁,适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别 不锁住资源,多个线程只有一个能修改资 ...

  3. 并发编程 20—— AbstractQueuedSynchronizer 深入分析

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  4. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

  5. Java并发包源码学习之AQS框架(一)概述

    AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.con ...

  6. 4.锁--Synchronizer Framework Base Class—AbstractQueuedSynchronizer介绍

    1. AQS简单介绍 AQS是Java并发类库的基础.其提供了一个基于FIFO队列,可以用于构建锁或者其它相关同步装置的基础框架.该同步器(下面简称同步器)利用了一个int来表示状态,期望它可以成为实 ...

  7. AbstractQueuedSynchronizer 原理分析 - 独占/共享模式

    1.简介 AbstractQueuedSynchronizer (抽象队列同步器,以下简称 AQS)出现在 JDK 1.5 中,由大师 Doug Lea 所创作.AQS 是很多同步器的基础框架,比如 ...

  8. AbstractQueuedSynchronizer AQS框架源码剖析

    一.引子 Java.util.concurrent包都是Doug Lea写的,来混个眼熟 是的,就是他,提出了JSR166(Java Specification RequestsJava 规范提案), ...

  9. 并发系列(4)之 AbstractQueuedSynchronizer 源码分析

    本文将主要讲述 AbstractQueuedSynchronizer 的内部结构和实现逻辑,在看本文之前最好先了解一下 CLH 队列锁,AbstractQueuedSynchronizer 就是根据 ...

随机推荐

  1. 2020ubuntu1804server编译安装redis笔记(三)启动服务和使用redis

    第一篇笔记记录了ubuntu1804server编译安装redis5,接下来要配置redis5了 网址:https://www.cnblogs.com/qumogu/p/12435694.html 第 ...

  2. JVM笔记-运行时内存区域划分

    1. 概述 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分为若干个不同的数据区域.它们各有用途,有些随着虚拟机进程的启动一直存在(堆.方法区),有些则随着用户线程的启动和结束而建立 ...

  3. 使用Jmeter进行压力测试结果偏差较大原因分析

    Apache软件基金会(ASF)是一家总部位于美国的非营利性慈善组织.ASF的所有产品都通过公共论坛的在线协作开发,并从美国境内的中央服务器分发.Jmeter是ASF的一款开源免费软件 ,在国内被很多 ...

  4. 【Weiss】【第03章】练习3.22、3.23、3.24:无代码题,栈的思考题

    [练习3.22] a.提出支持栈的Push和Pop操作以及第三种操作FindMin的数据结构,其中FindMin 返回该数据结构的最小元素,所有操作在最坏情况下的运行时间都是O(1). b.证明,如果 ...

  5. F-NAScan:一款网络资产扫描工具

    此脚本的大概流程为: ICMP存活探测-->端口开放探测-->端口指纹服务识别-->提取快照(若为WEB)-->生成结果报表 用法 python NAScan.py -h 10 ...

  6. 单片机的 HexToStr HexToBcd BcdToStr 几个转换函数

    今天写单片机一个程序 要检查一些数据,想发到串口调试的软件上在电脑上查看 有些转换函数 想网上找一个 看看都是很多的垃圾文章 很多的程序都不能用,那些发文章的人也不用心 所以我还是自己动手写一下吧 写 ...

  7. [bash]http serevr部署wordpress

    #!/bin/bash # echo "close selinux…" sed -i 's/Enforcing/disabled/' /etc/sysconfig/selinux ...

  8. 洛谷 P5176 公约数 题解

    原题链接 我天哪 大大的庆祝一下: 数论黑题 \(T1\) 达成! 激动地不行 记住套路:乱推 \(\gcd\),欧拉筛模板,然后乱换元,乱换式子,完了整除分块,欧拉筛和前缀和就解决了! \[\sum ...

  9. Leetcode_面试题62. 圆圈中最后剩下的数字(约瑟夫环)

    经典的约瑟夫环,n个人排成一圈,第m个出队. 递归 code1 class Solution { public: int f(int n,int m){ if(n==1){ //递归边界,最后一个 r ...

  10. CodeForces - 1244D 树(一条链)的染色

    题意:给一个无向的无环的树,需要用三种颜色将他染色,相邻的三个点不能有重复的颜色.给出每个点染成每种颜色的花费,求最小的染色花费,如果给的图不能按要求染色,输出-1. 思路:只有三种颜色,相邻三个点还 ...