深入理解java并发编程之aqs框架

news2024/11/5 16:26:50

在这里插入图片描述

跟synchronized 相比较,可重入锁ReentrankLock其实原理有什么不同?

  所得基本原理是为了达到一个目的;就是让所有线程都能看到某种标记。synchronized通过在对象头中设置标记实现了这一目的,是一种JVM原生的锁实现方式。而ReentrantLock以及所有的基于Lock接口的实现类,都是通过一个volitile修饰的int型变量,并保证每个线程都能拥有对该int的可见性和原子性修改,其本质都是基于AQS框架。

那么请谈谈AQS框架是怎么回事儿?

  AQS(AbstractQueuedSynchronizer类)是一个用来构建所和同步器的框架,各种的Lock包中的锁(常用的有 ReentrantLock、ReadWriteLock),以及其他的如Semaphore、CountDownLatch,甚至是早期的FutureTask等等,都是基于AQS来构建。

  • 1、AQS在内部定义了一个volatile int state 变量,表示同步状态;当线程调用lock方法的时候,如果state=0,说明没有任何线程占用共享资源的锁,可以获得锁并将state=1;如果state=1,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
  • 2、AQS通过Node内部类构成一个双向链表结构的同步队列,来完成线程获取锁的排队工作,当线程获取锁失败之后,就被添加到队列末尾。
    Node 类是对要访问同步代码的线程的封装,包含了线程本身及其状态叫waitStatus(有五种不同取值,分别表示是否被阻塞,是否等待唤醒,是否已经被取消等),每个Node结点关联器prev结点和next结点,方便线程释放锁后快速唤醒下一个在等待的线程,是一个FIFO的过程。
  • Node类有两个常量,SHARED和EXCLUSIVE,分别代表共享模式和独占模式。所谓共享模式是一个锁允许多条线程同时操作(信号量 Smaphore 就是基于AQS的共享模式实现的),独占模式是同一个时间段只能有一个线程对共享资源进行操作,多余的请求线程需要排队等待。
  • 3、AQS通过内部类ConditionObject 构建等待队列(可有多个),当Condition 调用wait()方法之后,线程将会加入等待队列中,而当Condition 调用 signal()方法之后,线程将从等待队列转移到同步队列中进行锁竞争。
  • 4、AQS和Condition各自维护了不同的队列,在使用Lock和Condition的时候,其实就是两个队列的互相移动。

请尽可能详细的对比一下synchronized和ReentrantLock的异同

  ReentrantLock 是Lock的实现类,是一个互斥的同步锁。

  从功能的角度上ReentrantLock比synchronized同步操作更加精细,甚至实现synchronized没有高级功能,例如

  • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,对处理执行时间非常长的同步块很有用
  • 带超时的获取锁尝试:在指定的时间范围内获取锁,如果时间到了仍然无法获取则返回。
  • 可以判断是否有线程在排队等待获取锁。
  • 可以响应中断请求:与synchronized不同,当获取到锁的线程被中断时,能够响应中断,中断异常将会被抛出,同时锁会被释放。
  • 可以实现公平锁。

  从锁释放酵素synchronized在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行出现异常的时候,JVM会自动释放锁定;但是使用Lock则不行,Lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally()代码中。

  从性能角度上来讲,Synchronized早期实现的比较低效,对比ReentrantLock,大多数的使用场景性能都较差。但是Java 6中对其进行了很多的改进,在竞争不激烈的时候,synchronized 的性能要优于 ReentrantLock;在高竞争的情况下,synchronized的性能会下降几十倍,但是ReentrantLock的性能则不变。

ReentrantLock是如何实现可重入性的?

  ReentrantLock内部自定义了同步器Sync(Sync即实现了AQS,又实现了AOS,而AOS提供了一种互斥锁持有的方式)也就是在加锁的时候使用了CAS算法,将线程对象放入到一个双向链表中,每次获取锁的时候,看一下当前维护的是那个线程ID和当前请求的线程ID是否一样,如果一样就可以获取到锁,如果不一样就无法获取到锁。

除了ReentrantLock,还有那些JUC中的并发工具?

  通常所说的并发包(JUC)也就是java.util.concurrent 及其子包,集中了Java并发的各种基础工具类,具体主要包括几个方面:

  • 提供了CountDownLatch、CyclicBarrier、Semaphore等等,这些都比synchronized更加高级,可以实现更加丰富的多线程控制同步结构。
  • 提供了ConcurrentHashMap、有序的ConcurrentSkipListMap、或者通过类似快照机制实现线程安全的动态数组CopyOnWriteArrayList等,各种线程安全的容器。
  • 提供了ArrayBlockingQueue、SynchronousQueue或者针对特定场景的PriorityBlockingQueue等等,各种并发队列实现。
  • 强大的Executor框架,可以创建各种不同类型的线程池,调度任务运行等等。

请谈一谈ReadWriteLock 和 StampedLock的区别。

  虽然ReentrantLock和synchronized使用简单,但是实现行为上有一定的局限性,要么不占,要么独占。实际应用场景中。有时候不需要大量竞争的写操作,而是以并发读取为主,为了进一步优化并发操作的粒度,Java提供了读写锁。

  读写锁基于的原理是多个读操作不需要互斥,如果读锁试图锁定时,写锁是被某个线程持有,读锁将无法获取,而只好等待对方操作结束,这样就可以自动保证不会读取到有争议的数据。

  ReadWriteLock代表了一对锁,下图是一个基于读写锁实现的数据结构,当数据量较大,并发读多,并发写少的时候。能够比纯同步版本凸显出优势。
在这里插入图片描述
  读写锁看起来比synchronized的粒度似乎更细一些,但在实际应用中,其表现也不是太好。主要还是因为相对比较大的开销。

  所以JDK在后期又引入了StampedLock,在提供类似读写锁的同时,还支持优化读模式,优化读基于假设,大多数情况下读操作并不会和写操作冲突,其逻辑是先试着修改,然后通过validate方法确认是否进行写模式,如果没有进入,就成功避免了开销;如果进入,则尝试获取读锁。
在这里插入图片描述

如何让Java线程彼此同步?了解过那些同步器?请分别介绍下。

  JUC中的同步器三个主要成员:CountDownLatch、CyclicBarrier和Semaphore,通过它们可以方便的实现很多线程之间的协助功能。CountDownLatch叫倒计数,允许一个或者多个线程等待某些操作完成。

  • 跑步比赛,裁判需要等到所有运动员都跑到终点才能计算成绩。
  • 模拟并发,需要启动100个线程同时访问某个地址,希望他们能并发执行而不是一个一个执行

  用法:CountDownLatch 构造方法指明计数数量,被等待线程调用countDown 将计数器减1,等待线程使用await进行线程等待。
在这里插入图片描述
  CyclicBarrier 叫做循环栅栏,它实现让一组线程等待至某个状态之后再全部同时执行,而且当所有等待线程被释放之后,CyclicBarrier可以被重复使用。CyclicBarrier比较典型的应用场景是用来等待并发线程结束。

  主要方法是await(),await()每被调用一次,计数则会减少1,并且阻塞当前线程。当计数器减至0 的时候,阻塞解除,所有在此CyclicBarrier上阻塞的线程开始运行。

  这之后,如果再次调用await(),计数就会变成n-1,新一轮重新开始,这便是Cyclic的含义,CyclicBarrier.await()带有返回值。用来表示当前线程是第一个到达这个Barrier的线程

在这里插入图片描述
  Semaphore ,Java版本的信号量实现,用于控制同时访问的线程个数,来达到限制通用资源访问的目的,其原理通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

在这里插入图片描述
  如果、Semaphore 的数值被初始化为1,那么一个线程就可以通过acquire进入互斥状态,本质上和互斥锁是非常相似的。但是区别也非常明显,比如互斥锁是持有者的,而对于Semaphore 这种计数器结构,虽然有类似功能,但其实不存在真正意义上的持有者,除非进行扩展包装。

CyclicBarrier 和CountDownLatch 看起来很相似,对比一下?

  首先它们的行为有相似度,主要区别如下

  • CountDownLatch 是不可以重置的,所以无法重用,CyclicBarrier没有这种限制,可以重用
  • CountDownLatch 的基本操作组合是countDown/await,调用await的线程阻塞等待countDown足够的次数,不管是一个线程还是多个线程countDown,只要次数足够就可以。CyclicBarrier的基本操作组合是await,当所有的伙伴都调用了await(),才会执行任务,并且自动重置。
  • CountDownLatch目的是让一个线程等待其他N个线程达到某个时间后自己再去做某个事情(通过CyclicBarrier 第二个构造方法 public CyclicBarrier(int parties,Runnable barrierAction),在新线程里做事可以达到同样的效果),而CyclicBarrier 的目的是让N多个线程相互等待知道所有的线程都打到某个状态,然后这N个线程再去执行后续的事情。(通过CountdownLatch在某些场合也能完成类似的效果)

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

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

相关文章

基于深度学习 卷积神经网络resnext50的中医舌苔分类系统

项目概述 本项目旨在通过深度学习技术,特别是利用卷积神经网络(Convolutional Neural Networks, CNNs)中的ResNeXt50架构,实现对中医舌象图像的自动分类。该系统不仅能够识别不同的舌苔类型,还能够在PyQt5框架下提供一…

IBM Storwize V7000存储控制器故障节点报错574

背景:由于客户机房搬迁,需要下电迁移设备。该存储自2016年投入生产使用后,从未关过机,已正常运行七八年时间,期间只更换过硬盘,无其他硬件故障。 在GUI界面点击关闭系统后,大概等了40分钟&…

AIGC简化文件管理:Python自动重命名Word和PDF文件

1.背景 大家应该也有遇到,自己电脑有很多文件命名不合理的文件,比如:文件1、想法3 ,当你长时间再看到这个文件的时候,已经很难知道文件内容。 今天我们将借助AIGC的编码能力,帮我们生成一个批量改文件名的…

多个路由器级联实现子网的方式

好久没写博客啦,最近搬家,换了网络环境,简单记录一下网络配置。 拓扑图就不画了,光猫 - > 华为TC7102路由 -> 华为AX2 Pro路由 -> 各种设备,简单表示就是这样。 原因是第一个路由是房东的,我希望自…

绿色能源在日常生活中的革新应用

绿色能源在日常生活中的革新应用 在当今社会,绿色能源已成为推动可持续发展的关键力量。它不仅代表着对环境的尊重与保护,更引领着生活方式的绿色转型。在我们的日常生活中,绿色能源正以多样化的形式,悄然改变着我们的生活方式。…

emlog Pro动态百分比顶部滚动条插件

插件介绍 给emlog博客顶部添加一个动态百分比顶部滚动条,可以选择设置图片、渐变背景样式。这个动态效果出现好久了,然后在js监测屏幕下拉值,最后用css改变图片和背景的宽度,从而实现动态的效果。目前支持10多种样式供选择&#…

iPhone手机清理软件:照片清理功能全解析

在数字化生活中,智能手机成为我们记录生活点滴的主要工具,尤其是iPhone,以其卓越的相机功能备受用户青睐。然而,成千上万的照片迅速堆积,不仅占用了大量存储空间,还使得设备运行缓慢。在众多解决方案中&…

软件工程-图书管理系统的概要设计

软件概要设计说明书 目录 软件概要设计说明书 一、引言 1.1 编写目的 1.2 背景 1.3 定义 1.3.1特定对象 1.3.2专业术语 1.4 参考资料 二、总体设计 2.1 需求规定 2.1.1信息要求 2.1.2功能要求 2.2 运行环境 2.3 基本概要设计和处理流程 2.4 体系结构设计 2.5 模…

Android Jetpact Lifecycle 解析

认识 Lifecycle Lifecycle 是什么 Lifecycle 是 Jetpack 组件库中的一个生命周期感知型组件。在 Lifecycle 出现之前,需要手动从外部宿主(如 Activity、Fragment 或自定义宿主)中将生命周期事件分发到功能组件内部,这势必会造成…

GEE数据集:加拿大卫星森林资源调查 (SBFI)-2020 年加拿大森林覆盖、干扰恢复、结构、物种、林分年龄以及 1985-2020 年林分替代干扰的信息

目录 简介 数据集后处理 数据下载链接 矢量属性 代码 代码链接 引用 许可 网址推荐 0代码在线构建地图应用 机器学习 加拿大卫星森林资源调查 (SBFI) 简介 卫星森林资源清查(SBFI)提供了 2020 年加拿大森林覆盖、干扰恢复、结构、物种、林分…

6.3图的遍历

图的遍历是指从某点出发,按照某种搜索方式沿着边访问图中所有节点 图的遍历算法主要有两种:广度优先,深度优先 都需要辅助数组visited[]来记录节点是否被访问过 6.3.1广度优先搜索 like层次遍历,需要辅助队列 代码实现 #include<stdio.h> #define maxnum 15 bool vi…

解决Win10版Township进度保存问题

解决Win10版Township进度保存问题 问题描述问题分析解决步骤1.WinR打开运行&#xff0c;输入regedit点击确定打开注册表2.进入注册表“计算机\HKEY_CURRENT_USER\Software\Classes\LocalSettings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings”目录3.在这…

算法题之水壶问题

水壶问题 有两个水壶&#xff0c;容量分别为 x 和 y 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 target 升。 你可以&#xff1a; 装满任意一个水壶清空任意一个水壶将水从一个水壶倒入另一个水壶&#xff0c;直到接水壶已满&#xff0c;或倒水壶已空。 示…

LabVIEW制系统开发流程介绍

在开发一个LabVIEW电机控制系统时&#xff0c;尤其是涉及多种类型的电机并需实现本地与远程控制时&#xff0c;合理的开发顺序是确保项目高效完成且返工最少的关键。下面介绍如何按照系统需求分阶段开发&#xff0c;从而保障开发的速度与质量&#xff0c;减少返工的风险。 开发…

企业大模型落地的“最后一公里”攻略

一、大模型落地的行业现状与前景 大模型在多个行业展现出强大的应用潜力。在金融行业&#xff0c;沉淀了大量高质量数据&#xff0c;各金融平台用户数以亿计&#xff0c;交易数据浩如烟海。利用大模型分析处理这些数据&#xff0c;金融机构可以预测用户行为偏好&#xff0c;更…

CMake/C++:一个日志库spdlog

项目仓库 GitHub - gabime/spdlog: Fast C logging library.Fast C logging library. Contribute to gabime/spdlog development by creating an account on GitHub.https://github.com/gabime/spdlog 知乎参考贴 https://zhuanlan.zhihu.com/p/674073158 先将仓库clone一下 然…

LabVIEW如何确保采集卡稳定运行

在LabVIEW开发中&#xff0c;如何确保硬件采集卡稳定运行&#xff0c;特别是长期采集电压信号&#xff0c;是系统稳定性的重要考虑因素。用户在使用采集卡时&#xff0c;可能需要频繁进行开始、停止和重新采集的操作&#xff0c;这对硬件和软件提出了高要求。下面介绍实现长期稳…

大数据开发职场:理性分析拖延

你有没有遇到过这样的情况&#xff1a;周四晚上&#xff0c;室友兴高采烈地邀请你去看最新上映的大片&#xff0c;而你正在奋战一份截止日期为下周一的化学作业。这个看似简单的选择&#xff0c;实际上隐藏着一个深刻的人生哲学问题。 目录 5秒钟抓住你的注意力深入探讨&#x…

YOLOv8/v10+DeepSORT多目标车辆跟踪(车辆检测/跟踪/车辆计数/测速/禁停区域/绘制进出线/绘制禁停区域/车道车辆统计)

01&#xff1a;YOLOv8 DeepSort 车辆跟踪 该项目利用YOLOv8作为目标检测模型&#xff0c;DeepSort用于多目标跟踪。YOLOv8负责从视频帧中检测出车辆的位置&#xff0c;而DeepSort则负责关联这些检测结果&#xff0c;从而实现车辆的持续跟踪。这种组合使得系统能够在视频流中准…

连接池的设计与实现-0基础Go语言版

为什么需要连接池&#xff1f; 假设现在没有连接池&#xff0c;每次建立一个新的连接&#xff0c;都需要消耗一定的时间开销&#xff08;必要时会使用TCP三次握手&#xff09;。因此&#xff0c;连接的创建和销毁是一件非常昂贵的操作。尤其是在高并发场景下&#xff0c;可能会…