Java基础--->并发部分(3)【JUC、AQS】

news2024/11/26 21:44:55

文章目录

  • AQS(AbstractQueuedSynchronizer)
    • AQS实现原理
    • AQS操作重点方法
  • Java并发容器JUC(java.util.concurrent)
    • ConcurrentHashMap
    • CopyOnWriteArrayList

AQS(AbstractQueuedSynchronizer)

AbstractQueuedSynchronizer (AQS) 是Java并发包中的一个核心类,这个类在 java.util.concurrent.locks 包下面。AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效的构造出同步器,是JUC中核心的组件。抽象同步队列是JVC其他锁实现的基础

AQS实现原理

​ 在类中维护一个state变量,然后还维护一个队列,以及获取锁,释放锁的方法
​ 当线程创建后,先判断state的值,如果为0,则没有线程使用,将state=1,执行完成后将state=0,期间如果有其他的线程访问,state=1的话,将其他的线程放入到队列中

state由于是多线程共享变量,所以必须定义成volatile,用来保证state的可见性,同时虽然volatile能保证可见性,但不能保证原子性,所以AQS提供了对state的原子操作方法,保证了线程安全。

AQS中实现的FIFO队列是双向链表实现的

​ 可以通过getState(),setState(),compareAndSetState进行操作

​ 分别时获取锁状态,设置锁状态,使用CAS机制设置状态

AQS操作重点方法

acquire:表示一定能获取锁

tryAcquire:尝试获取锁,如果获取锁成功,那么tryAcquire(arg)为false,说明已经获取锁,不用参与排队,也就是不用再执行后续判断条件,直接返回。

addWaiter:尝试获取锁失败后,将当前线程封装到一个Node对象中,添加到队尾,并返回Node节

acquireQueued:将线程添加到队列后,以自旋的方式去获取锁

release:释放锁

tryRelease:释放锁,将state值进行修改为0

unparkSuccessor:唤醒节点的后继者(如果存在)

AQS的锁模式分为:独占和共享

​ 独占锁:每次只能有一个线程持有锁,比如 ReentrantLock 就是以独占方式实 现的互斥锁。

​ 共 享 锁 : 允 许 多 个 线 程 同 时 获 取 锁 , 并 发 访 问 共 享 资 源 , 比 如 ReentrantReadWriteLock

Java并发容器JUC(java.util.concurrent)

JDK提供的大部分容器都在这个包中

里面的集合主要有:

  • ConcurrentHashMap : 线程安全的 HashMap
  • CopyOnWriteArrayList : 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector
  • ConcurrentLinkedQueue : 高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,这是一个非阻塞队列。
  • BlockingQueue : 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。
  • ConcurrentSkipListMap : 跳表的实现。这是一个Map,使用跳表的数据结构进行快速查找。

ConcurrentHashMap

在学习HashMap的时候知道了HashMap不是线程安全的,如果要实现线程安全,需要使用一个全局的锁来同步不同线程间的并发访问,所以非常影响性能,所有就有了ConcurrentHashMap,无论是读还是写,都能保证很高的性能,在读操作时几乎不需要加锁,在写操作时通过锁分段技术只对所操作的的段加锁而不影响客户端对其他段的访问。

在这里插入图片描述

JDK8之前的的底层采用分段的数组+链表实现,JDK8之后底层数据结构和HashMap8的一样,数组+链表/红黑树。

线程安全的实现的方式是,在JDK8之前ConcurrentHashMap对整个桶数组进行了分割(Segment,分段锁),每一把锁只锁一部分容器中的内容;在JDK8及之后放弃了Segment的理念,直接采用数组+链表/红黑树。

在JDK7及之前

一个ConcurrentHashMap里包含一个Segment数组,这个数组大小一旦初始化就不能改变。默认大小为16,也就是默认可以支持16个线程并发。ConcurrentHashMap 是由Segment数组和HashEntry数组组成,Segment继承了 ReentrantLock,所以Segment是一种可重入锁,HashEntry用于存储键值对数据。一个Segment包含一个HashEntry数组,一个HashEntry包含链表结构。每个Segment守护一个HashEntry数组里面的元素,如果要对数据并发写入会被阻塞,不同的Segment的写入可以并发执行的。

在JDK8及之后

ConcurrentHashMap取消了Segment分段锁,采用Node+CAS+Synchronized来保证安全。底层数据结构是数组+链表+红黑树。在这个版本中,Synchronized只锁定当前链表或红黑树的首节点,这样只要hash不冲突,就不会产生并发,就不会影响其他Node的读写,效率大幅提升。

ConcurrentHashMap不支持存储null键和null值,为了消除歧义

​ 原因:无法分辨是key没找到的null还是有key值的null,在多线程里面这是模糊不清的

放弃分段锁的原因

  1. 加入多个分段锁浪费内存空间。

  2. 生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。

    ​ 因此jdk8放弃了分段锁而是用了Node锁,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性,取代了锁。put时首先通过哈市找到对应链表后,查看是否第一个Node,如果是,直接用CAS原则插入,无需加锁,如果不是链表第一个Node,则直接用链表第一个Node加锁,这里加的锁是synchronized

CopyOnWriteArrayList

CopyOnWriteArrayList是线程安全的列表实现类,在操作时使用了一种特殊的方式来保证线程的安全即,写时复制。
在它进行写操作时,会将原来的数据复制一份,然后在该副本上进行操作,最后将副本重新赋值给CopyOnWriteArrayList实例的成员变量。使用这种写操作可以保证读操作的并发性,因为在写操作期间,读操作仍然访问的是原始数据,不会受到写操作的影响
由于CopyOnWriteArrayList采用了大量的内存复制来保证线程安全,因此写操作的性能较低,适用于读多写少的场景。同时,CopyOnWriteArrayList也不适用于需要实时反映最新数据的场景,因为写入操作的结果需要等待写操作完成之后才能被其他线程看到。

总的来说CopyOnWriteArrayList在读操作频繁、写操作较少、数据量不大的情况下,可以提供比较好的并发性能和线程安全性。

​ 使用ArrayList:线程不安全,在高并发情况下可能会出现问题
​ 使用Vector可以解决线程安全问题,还有Collections中的synchronizedList可以解决
​ 使用Vector效率低,原因是get()方法也加了锁,读操作多的情况下,效率低。
​ 使用CopyOnWriteArrayList:
​ 可以在读的时候不加锁,写的时候加锁,提高读的效率。实现过程在进行add,set等修改操作时,先将数据进行备份,对备份数据进行修改,之后将修改后的数据赋值给原数组。

源码分析

CopyOnWriteArrayList 读取操作的实现
读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,只会被另外一个 array 替换,因此可以保证数据安全。

     // The array, accessed only via getArray/setArray. 
     private transient volatile Object[] array;
     public E get ( int index){
         return get(getArray(), index);
     }
     @SuppressWarnings("unchecked")
     private E get (Object[]a,int index){
         return (E) a[index];
     }
     final Object[] getArray () {
         return array;
     }

CopyOnWriteArrayList 写入操作的实现
CopyOnWriteArrayList 写入操作 add()方法在添加集合的时候加了锁,保证了同步,避免了多线程写的时候会 copy 出多个副本出来。

		/**
      Appends the specified element to the end of this list.
         * @param e element to be appended to this list
         * @return {@code true} (as specified by {@link Collection#add})
      */
     public boolean add (E e){
         final ReentrantLock lock = this.lock;
         lock.lock();//加锁
         try {
             Object[] elements = getArray();
             int len = elements.length;
             Object[] newElements = Arrays.copyOf(elements, len + 1);//拷贝新数组
             newElements[len] = e;
             setArray(newElements);
             return true;
         } finally {
             lock.unlock();//释放锁
         }
     }

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

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

相关文章

如何从其他ETL工具迁移到ETLCloud上?

ETL数据集成工具主要用于将来自不同数据源的数据整合到一个单一的、一致的数据存储库或将数据分发到不同的数据源中,同时也可以把数仓中的数据通过ETL反向输出给业务系统使用。它可以帮助企业解决数据共享问题,同时有效地管理和利用海量数据,…

DAY 61 MySQL高级SQL语句

高级SQL语句(进阶查询) 先准备2个表 一个location表 use market;create table location(Region char(20),Store_Name char(20));insert into location values(East,Boston);insert into location values(East,New York);insert into location values(W…

python数据可视化显示(附代码)

Python是一种非常流行的编程语言,具有广泛的应用领域,包括数据可视化。在数据可视化中,Python提供了多种工具来帮助用户创建各种类型的图表、图形和可视化效果。本文将介绍Python数据可视化的基本概念、工具和技术,并提供代码示例…

CustomTkinter:【二】颜色和主题、外观模式、缩放、包装

GitHub地址: https://github.com/TomSchimansky/CustomTkinter 官网: https://customtkinter.tomschimansky.com/ 官方教程文档:https://customtkinter.tomschimansky.com/documentation/ 目录 1、颜色和主题2 、外观模式3 、缩放4、包装 1、颜色和主题 …

2023/5/21周报

目录 摘要 论文阅读 1、标题和现存问题 2、各个结构 3、基于GNN-LSTM-CNN 网络轨迹预测模型 4、实验准备 5、实验结果 深度学习 1、费舍尔判别 2、步骤具体化 3、GCN 总结 摘要 本周在论文阅读上,阅读了一篇基于GNN-LSTM-CNN网络的6G车辆轨迹预测算法的…

git pull报没有足够内存 not enough memory for initialization

git clone 或 git pull 批量同步远程 git仓库代码时,报 没有足够内存用于初始化 not enough memory for initialization。经过观察 资源管理器 的内存使用情况,发现为 剩余可用内存不足造成的。加物理内存麻烦,可通过适当调整 分页文件&…

chatgpt赋能Python-pythoncom安装

Pythoncom安装指南 如果你是一位Python编程的爱好者或专业工程师,那么你可能会需要使用Pythoncom库。Pythoncom是Python与COM技术相互操作的重要组件,它可以帮助你实现各种Windows应用程序与Python之间的无缝集成。 什么是Pythoncom Pythoncom是Pytho…

电商项目9:新增商品

电商项目9:新增商品 1、前端1.1、修复前端组件通信问题1.2、引入其他前端代码1.3、会员等级列表1.4、当前分类关联的所有品牌 2、后端2.1、会员系统搭建(注册与发现)2.2、当前分类关联的所有品牌2.3、获取分类下所有分组&关联属性 1、前端…

网上书店管理系统

系列文章 任务46 网上书店管理系统 文章目录 系列文章一、实践目的与要求1、目的2、要求 二、课题任务三、总体设计1.存储结构及数据类型定义2.程序结构3.所实现的功能函数4、程序流程图 四、小组成员及分工五、 测试添加新的图书购买信息显示所有图书购买信息按购买编号查找图…

软件测试—Selenium01

软件测试—Selenium01 🔎自动化测试自动化测试的定义自动化测试的分类 🔎SeleniumSelenium 是什么Selenium 的特点Selenium 的原理Selenium Java 的环境搭建Selenium 中常用的 APIBy.cssSelector()By.xpath()By.cssSelector() 与 By.xpath() 的比较 &am…

C++编译和链接

目录 一、源代码的组织 ①头文件(*.h) ②源文件(*.cpp) ③主程序(main函数所在的程序) ④从源代码到可执行文件,编译的过程有三大步骤: 1)编译预处理 2&#xff09…

Python学习笔记——《吴恩达Machine Learning》线性回归例程

文章目录 案例背景线性回归(Loss Regression)梯度下降法(批量梯度下降算法——batch gradient descent)计算成本函数和梯度下降使用线性回归拟合训练数据模型预测 梯度下降效果可视化完整版demo 案例背景 详情参照吴恩达机器学习…

Linux/Windows安装Maven

一、Linux部署Maven 注意:必须先安装jdk,maven与jdk(java -version)版本会有对应关系 版本对应(必看!):http://maven.apache.org/docs/history.html 官方tar包下载地址:h…

因为一个Bug,差点损失了100w

大家好,我是洋子 最近在做单接口的性能测试比较多,在压测过程发现了一个比较有意思的问题,拿出来和大家分享一下 背景是这样的,最近在搞线上的抽奖活动,压测的对象是一个抽奖接口,主要的逻辑见程序的流程…

Spring ioc容器

Spring ioc容器 导入 spring 容器包 使用 ioc 容器之前,需要先导入 Spring 的包 在 spring maven中下载 spring maven 网址:Maven Repository: spring (mvnrepository.com) 搜索 spring 找到 Spring Web MVC点击 spring-webmvc 进入 选择一个版本号点击&#x…

Ocean Optics USB2000光谱仪无法在Win10系统运行

1、问题描述 USB2000型光谱仪,由于生产年代过于久远,虽然能被Win10系统识别,但是驱动程序安装完成后依然报错, 提示:该设备无法启动。(代码 10) 请求USB BOS 描述符失败。 运行SpectraSuite软件…

ETLCloud社区版与Kettle对比分析

ETLCloud社区版本与Kettle社区版本都有什么优势和劣势? Pentaho Data Integration(PDI),也称为Kettle,是一款开源的数据集成工具,国内有很多企业都选择kettle作为数据清洗工具。以下是kettle的主要特点和优势: 广泛的…

python 绘制箱型图一些技巧

引言 本篇是之前有一个需求,需要用python来画箱型图,但要求很多,所以我也不断再版,今天突然想起来这个东西可以总结一下,正好马上得思考下一步做啥了,有足够的空闲时间,所以准备把一些基础概念…

【笔试强训day48】顺时针打印矩阵、左右最值最大差

博主简介:想进大厂的打工人博主主页:xyk:所属专栏: 笔试强训专栏 笔试强训 目录 文章目录 一、选择题 1.1 10.1.0.1/17的广播地址是( ) 1.2 网络地址172.16.22.38/28 请写出此地址的子网ID以及广播地址,此地址所处子网…

[2019“好贷杯“风控能力挑战赛一等奖] 基于神经网络算法的A股市场多因子选股的研究

本论文为 “2019年中国高校风险管理与控制能力挑战赛” 实际参赛作品,获得一等奖。 本论文体现了较好的数学建模思想和写作,所以将论文和完整代码进行了开源,方便与大家交流。