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. selenium中js定位

    学习selenium的时候经常用扫的定位方式WebDriver定位方式,但是一些Windows的窗口就无力了,这时候可以用js定位 使用js定位的时候是用DOM树定位方式 eg: document.g ...

  2. ggplot2(3) 语法突破

    3.1 简介 图形图层语法基于Wilkinson的图形语法,并在其基础上添加了许多新功能,使得图形更有表现力,并能完美地嵌入到R的环境中. 图形图层语法使得图形的重复更新变得简单——每次只更新一个特征 ...

  3. 【猫狗数据集】使用预训练的resnet18模型

    数据集下载地址: 链接:https://pan.baidu.com/s/1l1AnBgkAAEhh0vI5_loWKw提取码:2xq4 创建数据集:https://www.cnblogs.com/xi ...

  4. 数据库开发 Oracle与mysql间的批量处理接口 SSIS+存储过程实现

    公司目前不同的业务系统用了不同的数据库,涉及到oracle.mysql.sqlserver.而一些核心的业务在mysql中,所以平时经常要把oracle.sqlserver中的数据插入到mysql中. ...

  5. tkinter学习1

    GUI 用户交互界面 tkinter 介绍 tkinter是 python自带的gui库,对图像处理库tk的封装 #导入tkinter库 import tkinter #创建主窗口对象 root = ...

  6. 学习Shader所需的数学基础(坐标系,点和矢量)

    数学对于计算机图形学的重要性是不言而喻的.在学习Shader之前,首先就要打好数学基础,好在入门Unity Shader所需的数学知识都是线性代数中很基础的的内容.按部就班的来,第一篇文章记录总结的是 ...

  7. JVM的组成

    JVM一共有五大区域,程序计数器.虚拟机栈.本地方法栈.Java堆.方法区. 程序计数器 程序技术器是一块很小的内存空间,由于Java是支持多线程的.当线程数大于CPU数量时,CPU会按照时间片轮寻执 ...

  8. IPv4地址表示法详解

    在TCP/IP协议中,IP地址是一个最基本的概念,本文就来参考<计算机网络>谢希仁 这本书,总结一下IPv4地址表示法的发展阶段,做个读书笔记. IP地址的编址方法共经过了三个历史阶段: ...

  9. mvc5+ET6入门第一章

    这一篇主要讲的是MVC也就是Model--View--Controller的缩写,没有讲ET.其中 Model(模型)表示应用程序核心(比如数据库记录列表).通常模型对象在数据库中存取数据. View ...

  10. 环境篇:Docker

    环境篇:Docker www.docker.com Docker 是什么? Docker 是一个开源的应用容器引擎,基于Go语言并遵从Apache协议的开源,让开发者可以打包他们的应用以及依赖包到一个 ...