java并发之AQS详解(待更)

一、为什么要用AQS同步框架?

开发者如果不了解JMM和多线程编程,就会写出很多线程不安全的程序,即使是经验丰富的程序员,并发编程也难免会出错。

而对于java程序员来说,并发编程就变得容易得多了,因为并发编程大师Doug Lea为Java开发者提供了很多的并发容器和框架,而AQS就是java并发包下的一个核心框架。

AQS是抽象队列同步器,是用来构建Lock锁和同步组件的基础框架,JUC包下的很多锁和同步组件都是基于AQS构建的,比如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore。

为什么说AQS是构建Lock锁和同步组件的基础框架呢?

这里补充一点Lock和synchronized关键字的区别的知识:

  1. synchronized关键字是JVM层面上实现的,而Lock是JUC包下的一个接口
  2. synchronized让获取锁的线程执行完同步代码之后释放锁,线程执行发生异常的情况下,jvm会让线程释放锁,使用Lock的话,需要程序员显式地在finally块中释放锁,不然会造成死锁
  3. synchronized会让一直没获取到锁的线程阻塞等待,而Lock没有获取锁可以选择不用一直等待
  4. synchronized在发生异常的时候会自动释放占有的锁,不会出现死锁,Lock在发生异常的时候,不会主动释放占用的锁,必须手动unlock来释放锁,可能会引起死锁的发生
  5. synchronized无法判断锁的状态,而Lock可以判断
  6. synchronized是可重入锁,不响应中断,非公平的,而Lock是可重入锁,可判断,可公平的
  7. 在性能上,如果是并发量小的话,synchronized效率高,并发量高的话Lock高。Lock还可以提高多个线程读操作的效率(可以通过ReadWriteLock实现读写分离),ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步

设想一下,如果要实现线程的同步和锁机制,需要考虑哪些问题?

  • 如何去获取锁和释放锁?
  • 如何处理同步状态?
  • 竞争失败时线程如何处理?

而上述问题,AQS已经帮你解决了:

  • AQS实现了对同步状态的原子性管理
  • AQS实现了对线程阻塞和解除阻塞的管理
  • AQS实现了对同步队列的管理

Lock锁如何使用AQS?
AQS是实现锁的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。以ReentrantLock为例,它内部聚合了一个同步器Sync,这个同步器继承了AQS
在这里插入图片描述
如何使用AQS实现线程同步?
AQS定义了若干同步状态获取和释放的方法来供自定义同步组件使用,同步器的主要使用方式是继承,子类通过继承AQS并实现它的抽象方法来管理同步状态,在抽象方法实现中免不了对同步状态进行更改,这时就需要使用同步器提供的3个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))来进行操作,因为它们能够保证原子性的修改同步状态state字段。AQS既支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件。
在这里插入图片描述

二、什么是AQS同步框架?

AQS是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量state表示同步状态,通过内置的FIFO双向虚拟队列来完成资源获取线程的排队工作,并发包的作者Doug Lea期望AQS能够成为实现大部分同步需求的基础。

AQS使用的设计模式

AQS的设计是基于 模板方法模式 的,使用AQS需要继承它并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。

同步器可重写的方法有哪些?

在这里插入图片描述
在重写AQS指定的方法时,需要使用AQS提供的如下3个方法来访问或修改同步状态:

  • getState():获取当前的同步状态
  • setState(int newState):设置当前同步状态
  • compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性(CAS机制实现)
AQS提供的模板方法有哪些?(部分)

在这里插入图片描述
AQS提供的模板方法基本分为3类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程的情况。

三、AQS的底层原理(源码分析)

笔者打算从使用者的角度来分析AQS的底层,因此下面用AQS去实现一个简单的独占锁,独占锁就是在同一时刻只能有一个线程获取到锁,其他没有获取到锁的线程进入同步队列中等待。

用AQS实现一个简单的独占锁Mutex(代码来自AQS源码注释)
package com.demo.LockTest;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;public class Mutex implements Lock {//静态内部类,自定义同步器private static class Sync extends AbstractQueuedSynchronizer {//是否被占用protected boolean isHeldExclusively() {return getState() == 1;}//当状态为0的时候获取锁public boolean tryAcquire(int acquires) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}//释放锁,将状态设置为0protected boolean tryRelease(int releases) {//释放锁操作必须拥有锁,否则抛出异常if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true;}//返回一个Condition,每个Condition都包含了一个condition队列Condition newCondition() {return new ConditionObject();}}/*** 然后仅仅需要将同步操作代理到Sync上*/private final Sync sync = new Sync();@Overridepublic void lock() {sync.acquire(1);}@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}@Overridepublic boolean tryLock() {return sync.tryAcquire(1);}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}@Overridepublic void unlock() {sync.release(1);}public boolean isLocked() {return sync.isHeldExclusively();}public boolean hasQueuedThreads() {return sync.hasQueuedThreads();}@Overridepublic Condition newCondition() {return sync.newCondition();}
}

上面的独占锁Mutex是一个自定义的同步组件,Mutex中定义了一个静态内部类,该内部类继承了AQS并实现了独占式获取和释放同步状态。在tryAcquire(int acquire)方法中,使用CAS机制设置同步状态为1,并将当前线程标记为拥有锁的线程,否则进入同步队列中等待。tryRelease(int releases)方法中只是将同步状态设置为0,然后将拥有锁的线程设置为null。

在Mutex的实现中,可以发现,用户使用这个类的时候不会直接和内部同步器交互(被设为private),而是通过调用Mutex提供的方法来使用独占锁。而Mutex的方法实现仅仅是去调用了同步器中的模板方法而已,这样就大大降低了开发一个可靠自定义同步组件的门槛

AQS是怎么实现线程同步的?

从实现的角度分析,线程同步主要包括:

  • 同步队列
  • 独占式同步状态的获取与释放
  • 共享式同步状态的获取与释放
  • 超时获取同步状态等同步器的核心数据结构与模板方法
1)同步队列

AQS依赖内部的同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息封装成一个结点(Node),并将其加入同步队列,同时阻塞当前线程,当同步状态释放时,会把首结点唤醒,使其再次尝试获取同步状态。

同步队列中的节点(Node)用来保存获取同步状态失败的线程引用、等待状态以及前驱和后继节点。
在这里插入图片描述

  • Node prev:前驱节点,当节点加入同步队列时被设置(尾部添加)
  • Node next:后继节点
  • Thread waiter:等待队列中的后继节点,若当前节点是共享的,那么这个字段将是一个SHARED常量,即节点类型(独占和共享)和等待队列中的后继节点共用同一个字段(?)。
  • int status:等待状态,包含CACELLED(1)、SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)、INITIAL(0),后续会详细介绍这些状态

节点是构成同步队列(或等待队列)的基础,AQS拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部,同步队列的基本结构如下所示:
在这里插入图片描述
注意看,AQS包含了两个节点类型的引用,一个指向head节点,一个指向tail节点。

AQS将节点加入到同步队列

在这里插入图片描述

首结点的设置

同步队列遵循FIFO(先进先出)的,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态的时候,会去唤醒后继节点,而后继节点在获取到同步状态时就会将自己设置为首结点,过程如下:
在这里插入图片描述
请注意,设置首节点是通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置头节点的方法不需要CAS来保证也是线程安全的,它只需要将首节点设置成为原首节点的后继节点并断开原首节点的next引用即可。

2)独占式同步状态的获取和释放

通过调用AQS的acquire(int arg)方法可以获取同步状态,这个方法不响应中断,即线程获取同步状态失败后进入同步队列中,后续对该线程进行中断操作的时候,这个线程也不会从同步队列中移除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/102768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在雷电模拟器上安装Magisk并加载movecert模块抓https包(二)

接来下在PC端安装和配置Charles,方法同下面链接,不再赘述。在模拟器上安装magisk实现Charles抓https包(二)_小小爬虾的博客-CSDN博客 一、记录下本机IP和代理端口 二、在手机模拟器上设置代理192.168.31.71:8888,设置…

rv1126-rknpu-v1.7.3添加opencv库

rv1126所使用的rknn sdk里默认是不带opencv库的,官方所用的例程里也没有使用opencv,但是这样在进行图像处理的时候有点麻烦了,这里有两种办法: 一是先用python将所需要的图片处理好后在转化为bin格式文件,在使用c或c进行读取&…

vue配置@路径

第一步:安装path,如果node_module文件夹中有path就不用安装了 安装命令:npm install path --save 第二步:在vue.config.js文件(如果没有就新建)中配置 const path require("path"); function …

uniapp 获取地理位置(uni#getLocation和高德sdk获取中文地址)

参考 https://uniapp.dcloud.net.cn/api/location/location.html https://ask.dcloud.net.cn/article/35070 1. uniapp api获取经纬度 uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度: res.longitude);console.log(当前位…

前端模块化

当我们从公司招聘上看到要求模块化的内容。 能从这几个角度回答,就说明我们是懂模块化的。 1. 模块化的本意,是当遇到一个复杂项目的时候(简单的不建议用),把这个复杂的问题拆分成相对独立的模块,降低程序…

JavaScript中的异步编程

异步编程,是JavaScript编程中重要的一部分,最近学习了阮一峰老师的《深入掌握 ECMAScript 6 异步编程》系列文章,特意输出一篇学习笔记。传统的四种异步编程方法 回调函数 JavaScript 语言对异步编程的实现,就是回调函数。所谓回…

MyBatis的< resultMap >标签的简析

<resultMap> 标签的作用是用于配置数据库表和实体类之间的映射关系&#xff0c;定义查询结果集与实体类的属性之间的映射关系。它定义了 SQL 查询语句在返回结果集合时&#xff0c;如何将列名和属性名匹配起来&#xff0c;并将结果封装进一个指定的对象中。在 MyBatis 中…

设计模式-状态模式

介绍 一个对象有状态变化每次状态变化都会触发一个逻辑不能总是用if else来控制 示例 交通信号灯不同颜色的变化 UML类图 传统UML类图 简化后的UML类图 代码演示 // 状态&#xff08;红灯、绿灯、黄灯&#xff09; class State {constructor(color) {this.color col…

软件工程概论

文章目录 软件的定义软件的特点软件的种类软件工程的起源软件工程的三个阶段软件工程概念的提出软件开发的本质软件工程框架软件工程的目标软件工程的原则软件工程的活动 软件的定义 计算机系统中的程序及其文档。 程序是计算任务的处理对象和处理规则的描述&#xff1b; 文档…

SQL基本语法用例大全

文章目录 SQL语法概述简单查询计算列查询条件查询范围查询使用逻辑运算符过滤数据使用IN操作符过滤数据格式化结果集模糊查询行数据过滤数据排序数据统计分析分组总计简单子查询多行子查询多表链接插入数据更新和删除数据使用视图数据库管理数据表管理 SQL语法概述 SQL(Struct…

腾讯云优惠券种类、领取方法及使用教程分享

腾讯云是国内领先的云计算服务提供商&#xff0c;为用户提供丰富的云计算产品和服务。为了吸引更多用户使用腾讯云的产品和服务&#xff0c;腾讯云会定期推出各种优惠券活动。本文将为大家介绍腾讯云优惠券的种类、领取方法及使用教程。 一、腾讯云优惠券种类介绍 腾讯云优惠券…

Ubuntu的中文乱码问题

一、Ubuntu的中文乱码问题 sudo apt-get install language-pack-zh-hans 二、修改/etc/environment&#xff08;在文件的末尾追加&#xff09;&#xff1a; LANG"zh_CN.UTF-8" LANGUAGE"zh_CN:zh:en_US:en" 三、修改/var/lib/locales/supported.d/loca…

“.NET视频总结:认识框架的结构和组件,掌握开发工具的奥妙“

目录 第一单元&#xff1a;二十一世纪程序执行 背景: 总结&#xff1a; 第二单元:对象导向与类别设计 背景: 总结&#xff1a; 第三单元&#xff1a;使用类别与基底类别库 背景: 总结: 第四单元:Windows开发程序 背景: 总结: 第五单元:防护式程序设计 背景: 总结…

SXSSFWorkbook-MinIo-大数据-流式导出

文章目录 前言业务现状架构思路技术细节生成摘要IDSXSSFWorkbookMinIomybatis 流查询PipedInputStream 保存到minio 总结 前言 由于业务涉及到数据比较大&#xff0c;用户对导出功能使用频繁&#xff0c;每次导出数据两10万以上。 为了减少数据库压力&#xff0c;及应用服务器…

代理IP端口是什么意思呢?

今天&#xff0c;咱们来聊聊一个小众但很有料的话题——代理IP端口&#xff0c;它可是你纵横互联网世界的好搭子哦&#xff01; 首先&#xff0c;我们得先弄明白&#xff0c;代理IP端口是个啥? 代理IP端口就像是通往网络世界的门票&#xff0c;是你和代理服务器之间的桥梁。…

API接口安全运营研究(内附官方开发平台api接口接入方式)

摘 要 根据当前API技术发展的趋势&#xff0c;从实际应用中发生的安全事件出发&#xff0c;分析并讨论相关API安全运营问题。从风险角度阐述了API接口安全存在的问题&#xff0c;探讨了API检测技术在安全运营中起到的作用&#xff0c;同时针对API安全运营实践&#xff0c;提出…

关于系统/网络运维面试经验总结

一. 熟悉Linux命令 1. 最最最常问到的是 如何查看系统内存占用情况&#xff1f; ① free命令&#xff1a;free [-h][-m] 显示系统的内存使用情况&#xff0c;包括总内存、已使用内存、空闲内存等信息。其中&#xff0c;-m选项是以MB为单位来展示内存使用信息&#xff1b;-h选…

Ubuntu-Ports更新源 ARM64更新源

Ubuntu-Ports更新源 Ubuntu ARM64更新源 简介&#xff1a; Arm64&#xff0c;Armhf等平台的Ubuntu软件仓库。 Ubuntu-Ports国内镜像源 华为镜像Ubuntu-Ports 阿里云镜像Ubuntu-Ports 清华大学镜像Ubuntu-Ports 改用清华大学镜像更新源 Ubuntu 的软件源配置文件是 /etc/ap…

办公室人人在用的iTab桌面真的好用吗?

本人坐标北京&#xff0c;在一家中型互联网公司当社畜多年。最近发现一个奇怪的现象&#xff0c;我工位前后左右的同事都跟我在用一样的浏览器桌面——iTab新标签页。我表示莫非真的英雄所见略同&#xff1f; 我是去年1月份在刷B站时偶然刷到一条评论&#xff0c;有人分享自己…

日语学习网站web项目

支持日语五十音,平片假名、罗马音、词义转换、百度翻译功能,方便日语初学者学习日语发音 介绍 采用vitevue3ts技术栈开发, pinia管理全局化。主要是为了日语入门学习五十音, 以及日语句子罗马 发音对照练习。 参考以下两个项目完成 https://github.com/hexenq/kuroshiro ht…