当前位置: 首页 > news >正文

java并发编程原理2 (AQS, ReentrantLock,线程池)

一、AQS:

1.1 AQS是什么?

AQS就是一个抽象队列同步器,abstract queued sychronizer,本质就是一个抽象类。

AQS中有一个核心属性state,其次还有一个双向链表以及一个单项链表。

首先state是基于volatile修饰,再基于CAS修改,同时可以保证三大特性。(原子,可见,有序)

其次还提供了一个双向链表。有Node对象组成的双向链表。

最后在Condition内部类中,还提供了一个由Node对象组成的单向链表。

AQS是JUC下大量工具的基础类,很多工具都基于AQS实现的,比如lock锁,CountDownLatch,Semaphore,线程池等等都用到了AQS。


state是啥:state就是一个int类型的数值,同步状态,至于到底是什么状态,看子类实现。

condition和单向链表是啥:都知道sync内部提供了wait方法和notify方法的使用,lock锁也需要实现这种机制,lock锁就基于AQS内部的Condition实现了await和signal方法。(对标sync的wait和notify)


sync在线程持有锁时,执行wait方法,会将线程扔到WaitSet等待池中排队,等待唤醒

lcok在线程持有锁时,执行await方法,会将线程封装为Node对象,扔到Condition单向链表中,等待唤醒


Condition在做了什么:将持有锁的线程封装为Node扔到Condition单向链表,同时挂起线程。如果线程唤醒了,就将Condition中的Node扔到AQS的双向链表等待获取锁。

1.2 唤醒线程时,AQS为什么从后往前遍历?

如果线程没有获取到资源,就需要将线程封装为Node对象,安排到AQS的双向链表中排队,并且可能会挂起线程

如果在唤醒线程时,head节点的next是第一个要被唤醒的,如果head的next节点取消了,AQS的逻辑是从tail节点往前遍历,找到离head最近的有效节点?

想解释清楚这个问题,需要先了解,一个Node对象,是如何添加到双向链表中的。

基于addWaiter方法中,是先将当前Node的prev指向tail的节点,再将tail指向我自己,再让prev节点指向我

如下图,如果只执行到了2步骤,此时,Node加入到了AQS队列中,但是从prev节点往后,会找不到当前节点。

image.png

1.3 AQS为什么用双向链表,(为啥不用单向链表)?

因为AQS中,存在取消节点的操作,节点被取消后,需要从AQS的双向链表中断开连接。

还需要保证双向链表的完整性,

  • 需要将prev节点的next指针,指向next节点。
  • 需要将next节点的prev指针,指向prev节点。

如果正常的双向链表,直接操作就可以了。

但是如果是单向链表,需要遍历整个单向链表才能完成的上述的操作。比较浪费资源。

1.4 AQS为什么要有一个虚拟的head节点

有一个哨兵节,点更方便操作。

另一个是因为AQS内部,每个Node都会有一些状态,这个状态不单单针对自己,还针对后续节点

  • 1:当前节点取消了。
  • 0:默认状态,啥事没有。
  • -1:当前节点的后继节点,挂起了。
  • -2:代表当前节点在Condition队列中(await将线程挂起了)
  • -3:代表当前是共享锁,唤醒时,后续节点依然需要被唤醒。

Node节点的ws,表示很多信息,除了当前节点的状态,还会维护后继节点状态。

如果取消虚拟的head节点,一个节点无法同时保存当前阶段状态和后继节点状态。

同时,在释放锁资源时,就要基于head节点的状态是否是-1。来决定是否唤醒后继节点。

如果为-1,正常唤醒

如果不为-1,不需要唤醒吗,减少了一次可能发生的遍历操作,提升性能。

1.5 ReentrantLock的底层实现原理

ReentrantLock是基于AQS实现的。

在线程基于ReentrantLock加锁时,需要基于CAS去修改state属性,如果能从0改为1,代表获取锁资源成功

如果CAS失败了,添加到AQS的双向链表中排队(可能会挂起线程),等待获取锁。

持有锁的线程,如果执行了condition的await方法,线程会封装为Node添加到Condition的单向链表中,等待被唤醒并且重新竞争锁资源

Java中除了一会讲到的线程池中Worker的锁之外,都是可重入锁。

1.6 ReentrantLock的公平锁和非公平锁的区别

  • 公平锁和非公平中的lock方法和tryAcquire方法的实现有一内内不同,其他都一样
    • 非公平锁lock:直接尝试将state从 0 ~ 1,如果成功,拿锁直接走,如果失败了,执行tryAcquire
    • 公平锁lock:直接执行tryAcquire
    • 非公平锁tryAcquire:如果当前没有线程持有锁资源,直接再次尝试将state从 0 ~ 1如果成功,拿锁直接走
    • 公平锁tryAcquire:如果当前没有线程持有锁资源,先看一下,有排队的么。
      • 如果没有排队的,直接尝试将state从 0 ~ 1
      • 如果有排队的,第一名不是我,不抢,继续等待。
      • 如果有排队的,我是第一名,直接尝试将state从 0 ~ 1
    • 如果都没拿到锁,公平锁和非公平锁的后续逻辑是一样的,排队后,就不存在所谓的插队。

生活的例子:非公平锁会有机会尝试强行获取锁资源两次,成功开开心心走人,失败,消消停停去排队。

  • 有个人前来做核酸
    • 公平锁:先看眼,有排队的么,有就去排队
    • 非公平锁:不管什么情况,先尝试做凳子上。如果坐上了,直接被扣,扣完走人,如果没做到凳子上
      • 有人正在扣嗓子眼么?
        • 没人正在被扣,上去尝试做凳子上!成功了,扣完走人。
        • 如果有人正在扣,消停去排队。

1.7 ReentrantReadWriteLock如何实现的读写锁

如果一个操作写少读多,还用互斥锁的话,性能太低,因为读读不存在并发问题。

怎么解决啊,有读写锁的出现。

ReentrantReadWriteLock也是基于AQS实现的一个读写锁,但是锁资源用state标识。

如何基于一个int来标识两个锁信息,有写锁,有读锁,怎么做的?

一个int,占了32个bit位。

在写锁获取锁时,基于CAS修改state的低16位的值。

在读锁获取锁时,基于CAS修改state的高16位的值。

写锁的重入,基于state低16直接标识,因为写锁是互斥的。

读锁的重入,无法基于state的高16位去标识,因为读锁是共享的,可以多个线程同时持有。所以读锁的重入用的是ThreadLocal来表示,同时也会对state的高16为进行追加。

二、阻塞队列高频问题:

2.1 阻塞队列

ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue

ArrayBlockingQueue:底层基于数组实现,记得new的时候设置好边界。

LinkedBlockingQueue:底层基于链表实现的,可以认为是无界队列,但是可以设置长度。

PriorityBlockingQueue:底层是基于数组实现的二叉堆,可以认为是无界队列,因为数组会扩容。

ArrayBlockingQueue,LinkedBlockingQueue是ThreadPoolExecutor线程池最常用的两个阻塞队列。

PriorityBlockingQueue:是ScheduleThreadPoolExecutor定时任务线程池用的阻塞队列跟PriorityBlockingQueue的底层实现是一样的。(其实本质用的是DelayWorkQueue)

2.2 虚假唤醒

虚假唤醒在阻塞队列的源码中就有体现。

比如消费者1在消费数据时,会先判断队列是否有元素,如果元素个数为0,消费者1会挂起。

此处判断元素为0的位置,如果用if循环会导致出现一个问题。

如果生产者添加了一个数据,会唤醒消费者1。

但是如果消费者1没拿到锁资源,消费者2拿到了锁资源并带走了数据的话。

消费者1再次拿到锁资源时,无法从队列获取到任何元素。导致出现逻辑问题。

解决方案,将判断元素个数的位置,设置为while判断。

三、线程池

3.1 线程池的7个参数(不会就回家等通知)

核心线程数,最大线程数,最大空闲时间,时间单位,阻塞队列,线程工厂,拒绝策略

3.2 线程池的状态有什么,如何记录的?

线程池不是什么时候都接活的!
线程池有5个状态。、
image.png

线程池的状态是在ctl属性中记录的。本质就是int类型
image.png

ctl的高三位记录线程池状态

低29位,记录工作线程个数。即便你指定的线程最大数量是Integer.MAX_VALUE他也到不了

3.3 线程池常见的拒绝策略(不会就回家等通知)

AbortPolicy:抛异常(默认)

image.png

CallerRunsPolicy,谁提交的任务,谁执行。异步变同步

image.png

DiscardPolicy:任务直接不要

image.png

DiscardOldestPolicy:把最早放过来的任务丢失,再次尝试将当前任务交给线程池处理

image.png

一般情况下,线程池自带的无法满足业务时,自定义一个线程池的拒绝策略。

实现下面的接口即可。

image.png

3.4 线程池执行流程(不会就回家等通知)

核心线程不是new完就构建的,是懒加载的机制,添加任务才会构建核心线程

2个核心线程 5个最大线程 阻塞队列长度为2

image.png

3.5 线程池为什么添加空任务的非核心线程

image.png

避免线程池出现工作队列有任务,但是没有工作线程处理。

线程池可以设置核心线程数是0个。这样,任务扔到阻塞队列,但是没有工作线程,这不凉凉了么~~

线程池中的核心线程不是一定不会被回收,线程池中有一个属性,如果设置为true,核心线程也会被干掉

image.png

3.6 在没任务时,线程池中的工作线程在干嘛?

线程会挂起,默认核心线程是WAITING状态,非核心是TIMED_WAITING

如果是核心线程,默认情况下,会在阻塞队列的位置执行take方法,直到拿到任务为止。

如果是非核心线程,默认情况下,会在阻塞队列的位置执行poll方法,等待最大空闲时间,如果没任务,直接拉走咔嚓掉,如果有活,那就正常干。

3.7 工作线程出现异常会导致什么问题?

是否抛出异常、影响其他线程吗、工作线程会嘎嘛?

如果任务是execute方法执行的,工作线程会将异常抛出。

如果任务是submit方法执行的futureTask,工作线程会将异常捕获并保存到FutureTask里,可以基于futureTask的get得到异常信息

出现异常的工作线程不会影响到其他的工作线程。

runWorker中的异常会被抛到run方法中,run方法会异常结束,run方法结束,线程就嘎了!

如果是submit,异常没抛出来,那就不嘎~

3.8 工作线程继承AQS的目的是什么?

工作线程的本质,就是Worker对象

继承AQS跟shutdown和shutdownNow有关系。

如果是shutdown,会中断空闲的工作线程,基于Worker实现的AQS中的state的值来判断能否中断工作线程。

如果工作线程的state是0,代表空闲,可以中断,如果是1,代表正在干活。

如果是shutdownNow,直接强制中断所有工作线程

3.9 核心参数怎么设置?

线程池的目的是为了充分发挥CPU的资源。提升整个系统的性能。

系统内部不同业务的线程池参考的方式也不一样。

如果是CPU密集的任务,一般也就是CPU内核数 + 1的核心线程数。这样足以充分发挥CPU性能。

如果是IO密集的任务,因为IO的程度不一样的啊,有的是1s,有的是1ms,有的是1分钟,所以IO密集的任务在用线程池处理时,一定要通过压测的方式,观察CPU资源的占用情况,来决定核心线程数。一般发挥CPU性能到70~80足矣。所以线程池的参数设置需要通过压测以及多次调整才能得出具体的。

比如一个业务要查询三个服务

相关文章:

java并发编程原理2 (AQS, ReentrantLock,线程池)

一、AQS: 1.1 AQS是什么? AQS就是一个抽象队列同步器,abstract queued sychronizer,本质就是一个抽象类。 AQS中有一个核心属性state,其次还有一个双向链表以及一个单项链表。 首先state是基于volatile修饰&#x…...

研报精选230219

目录 【行业230219山西证券】煤炭行业周报:复工改善,港口价格企稳反弹【行业230219中航证券】农林牧渔行业周观点:一号文件落地,生物育种超势不改【行业230219华西证券】汽车行业周报:新车密集上市 自主转型提速【个股…...

【PPPoE】PPPoE拨号流程

简介 PPPoE(Point-to-Point Protocol over Ethernet)是一种在以太网上封装PPP协议的方式,常用于在宽带接入中进行拨号。 PPPoE的拨号原理如下: 客户端发起PPPoE Active Discovery Initiation (PADI)报文,广播到网络…...

django项目实战(django+bootstrap实现增删改查)

目录 一、创建django项目 二、修改默认配置 三、配置数据库连接 四、创建表结构 五、在app当中创建静态文件 六、页面实战-部门管理 1、实现一个部门列表页面 2、实现新增部门页面 3、实现删除部门 4、实现部门编辑功能 七、模版的继承 1、创建模板layout.html 1&…...

Lesson4---Python语言基础(2)

4.1 内置数据结构 4.1.1 序列数据结构(sequence) 成员是有序排列的每个元素的位置称为下标或索引通过索引访问序列中的成员Python中的序列数据类型有字符串、列表、元组 “abc” ≠ “bac” 4.1.1.1 创建列表和元组 Python中的列表和元组&#xff0c…...

NCHW - NHWC - CHWN 排列

TensorFlow有两种数据格式NHWC和NCHW,默认的数据格式是NHWC,可以通过参数data_format指定数据格式。这个参数规定了 input Tensor 和 output Tensor 的排列方式。 1、data_format 设置为 “NHWC” 时,排列顺序为 [batch, height, width, channels] 设置为 “NCHW” 时,排…...

2019蓝桥杯真题矩阵切割(填空题) C语言/C++

题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 小明有一些矩形的材料,他要从这些矩形材料中切割出一些正方形。 当他面对一块矩形材料时,他总是从中间切割一刀,切出一块最大的正…...

Java线程池的创建以及原理

一、为什么要使用线程池 在外面的日常开发中,也使用了不少池化技术,比如线程池、数据库连接池、HTTP连接池等等都是对这个思想的应用。 池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。 线程池提供了一种限制和管理资…...

Java集合学习之Map

1.什么是Map Java里的Map接口是一个集合根接口,表示一个 键值对(Key-Value) 的映射。 简单来说就是键和值是一对的,每一个 Key都有唯一确定的 Value对应。 其中要求 键(Key) 唯一,因为是按照…...

java 基于maven多模块合并打包部署

项目环境 jdk 1.8spring 2.7.xmaven 3.6 项目结构 模块功能 client – 对外service common – 共用工具切面等 main – 启动类 goods – 子模块具体实现 模块间依赖关系 client – 无依赖 common – 无依赖 main – client、common、goods goods – client、common 具体P…...

Kubernetes是个什么东东?

Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语,意…...

【go语言grpc之client端源码分析三】

go语言grpc之server端源码分析三newClientStreamnewAttemptLocked上一篇在介绍了grpc.Dial之后,然后再介绍一下后面的 //创建RPC客户端client : pb.NewGreetsClient(conn)//设置超时时间_, cancel : context.WithTimeout(context.Background(), time.Second)defer c…...

Android 基础知识4-2.6LinearLayout(线性布局)

一、LinearLayout的概述 线性布局(LinearLayout)主要以水平或垂直方式来排列界面中的控件。并将控件排列到一条直线上。在线性布局中,如果水平排列,垂直方向上只能放一个控件,如果垂直排列,水平方向上也只能…...

补充前端面试题(三)

图片懒加载<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, in…...

.net开发安卓入门-自动升级(配合.net6 webapi 作为服务端)

文章目录思路客户端权限清单&#xff08;AndroidManifest.xml&#xff09;权限列表(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;打开外部应用的权限(完整内容看 权限清单&#xff08;AndroidManifest.xml&#xff09;&#xff09;添加文件如下…...

分享111个HTML艺术时尚模板,总有一款适合您

分享111个HTML艺术时尚模板&#xff0c;总有一款适合您 111个HTML艺术时尚模板下载链接&#xff1a;https://pan.baidu.com/s/1sYo2IPma4rzeku3yCG7jGw?pwdk8dx 提取码&#xff1a;k8dx Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 时尚理发沙龙服务网站模…...

spring之Spring AOP基于注解

文章目录前言一、Spring AOP基于注解的所有通知类型1、前置通知2、后置通知3、环绕通知4、最终通知5、异常通知二、Spring AOP基于注解之切面顺序三、Spring AOP基于注解之通用切点三、Spring AOP基于注解之连接点四、Spring AOP基于注解之全注解开发前言 通知类型包括&#x…...

LeetCode题目笔记——6362. 合并两个二维数组 - 求和法

文章目录题目描述题目链接题目难度——简单方法一&#xff1a;常规双指针遍历代码/Python方法二&#xff1a;字典\哈希表代码/Python总结题目描述 给你两个 二维 整数数组 nums1 和 nums2. nums1[i] [idi, vali] 表示编号为 idi 的数字对应的值等于 vali 。nums2[i] [idi, …...

【C#基础】C# 常用语句讲解

序号系列文章3【C#基础】C# 数据类型总结4【C#基础】C# 变量和常量的使用5【C#基础】C# 运算符总结文章目录前言语句概念1&#xff0c;迭代语句1.1 for 语句1.2 foreach 语句1.3 while 语句1.4 do 语句2&#xff0c;选择语句2.1&#xff0c;if 语句2.2&#xff0c;else 语句2.3…...

腾讯云——负载均衡CLB

负载均衡 CLB 提供四层&#xff08;TCP 协议/UDP 协议/TCP SSL 协议&#xff09;和七层&#xff08;HTTP 协议/HTTPS 协议&#xff09;负载均衡。您可以通过 CLB 将业务流量分发到多个后端服务器上&#xff0c;消除单点故障并保障业务可用性。CLB 自身采用集群部署&#xff0c;…...

6.关于系统服务的思考—— native vs java

文章目录native服务 以sensor service为例Native 服务java 服务&#xff0c; 以vibrate为例java 服务 以一个demo为例native服务 以sensor service为例 service启动 SystemServer.startBootstrapServices---->>>mSystemServiceManager.startService—>>>Sen…...

SQL语句创建视图:

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍数据库中有关视图的知识,参考学校作业. 金句分享:…...

使用BP神经网络和Elman Net预测航班价格(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...

JavaWeb9-volatile解决内存可见性和指令重排序问题

目录 1.解决内存可见性问题 2.解决指令重排序问题 3.volatile缺点 4.特使使用场景 volatile&#xff08;易变的&#xff0c;易挥发的&#xff0c;不稳定的&#xff09;可以解决内存可见性和指令重排序的问题。 1.解决内存可见性问题 代码在写入 volatile 修饰的变量时&am…...

Docker - 镜像操作命令

镜像名称一般分为两部分组成:[repository]:[tag]在没有指定tag时&#xff0c;默认是latest&#xff0c;代表最新版本的镜像1.下载docker镜像 docker pull repository:tag2.查看本地所有镜像 docker images3.创建镜像别名 docker tag repository:tag repository111:tag4.查看镜像…...

全栈之路-前端篇 | 第三讲.基础前置知识【前端标准与研发工具】学习笔记

欢迎关注「全栈工程师修炼指南」公众号 点击 &#x1f447; 下方卡片 即可关注我哟! 设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01; 涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识 “ 花开堪折直须折&#xff0c;莫待无花…...

Tomcat 线上调优记录

原始Tomcat配置 启动参数Plaintext-Xms256m -Xmx512m -XX:MaxPermSize128m Tomcat 参数配置XML<Executor name"tomcatThreadPool" namePrefix"catalina-exec-" maxThreads"1500" minSpareThreads"50" maxIdleTime"600000&q…...

学习 Python 之 Pygame 开发坦克大战(四)

学习 Python 之 Pygame 开发坦克大战&#xff08;四&#xff09;坦克大战添加音效1. 初始化音效2. 加入游戏开始音效和坦克移动音效3. 添加坦克开火音效4. 添加装甲削减音效5. 添加坦克爆炸音效6. 添加子弹击中边界音效坦克大战添加音效 我的素材放到了百度网盘里&#xff0c;…...

New和Malloc的使用及其差异

1&#xff0c;new的使用关于new的定义&#xff1a;new其实就是告诉计算机开辟一段新的空间&#xff0c;但是和一般的声明不同的是&#xff0c;new开辟的空间在堆上&#xff0c;而一般声明的变量存放在栈上。通常来说&#xff0c;当在局部函数中new出一段新的空间&#xff0c;该…...

2023年细胞生物学复习汇总

细胞分化 1.什么是细胞分化&#xff1f;细胞分化的特点是什么&#xff1f; 答&#xff1a;&#xff08;1&#xff09;细胞分化&#xff08;cell differentiation&#xff09;是指同一来源的细胞逐渐产生出形态结构、功能特征各不相同的细胞类群的过程&#xff0c;其结果是在空间…...

asp企业网站模板/常见的网络营销方式有哪几种

浏览器地址栏输入URL到显示页面发生了什么&#xff1f; 1.用户输入 2.卸载原页面并重定向到新页面 3.处理Service Worker 4.网络请求 5.服务端响应 6.浏览器渲染详细流程 这个过程分为两个部分&#xff1a;网络通信页面渲染 步骤1&#xff1a;DNS 域名解析&#xff08;域名解析…...

直播网站app开发/app开发费用一览表

SDK Manager 打不开, "系统找不到指定路径 " "Unable to access jarfile lib\archquery.jar"问题的解决办法参考文章&#xff1a; &#xff08;1&#xff09;SDK Manager 打不开, "系统找不到指定路径 " "Unable to access jarfile lib\ar…...

怎么做网站旺铺装修/微博搜索引擎优化

一、WXML节点布局相交状态(通过微信的API实现懒加载) 节点布局相交状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。 节点布局相交状态 API中有一个 wx.createIntersectionO…...

58同城 网站建设/最近重大新闻

这是一种沿行复制元素的方法,为我们提供了一个2D数组.然后,我们将用大数字填充上三角区域,以便稍后当我们沿着每一行对数组进行排序时,基本上将所有元素排序到对角线元素并模拟累积窗口.然后,按照选择中间一个的中位数或两个中间一个的平均值的定义(对于偶数没有元素),我们将得…...

wordpress如何开启page页面评论/抖音引流推广怎么做

2019独角兽企业重金招聘Python工程师标准>>> 概述 正则表达式是一种描述字符串结果的语法规则&#xff0c;是一个特定的格式化模式&#xff0c;可以匹配、替换、截取匹配的字符串。常用的语言基本上都有正则表达式&#xff0c;如JavaScript、java等。其实&#xff0…...

web网站建设报价/微信公众号seo

基本概念所谓完美哈希函数。就是指没有冲突的哈希函数。即对随意的 key1 ! key2 有h(key1) ! h(key2)。设定义域为X&#xff0c;值域为Y, n|X|,m|Y|。那么肯定有m>n,假设对于不同的key1,key2属于X,有h(key1)!h(key2)&#xff0c;那么称h为完美哈希函数&#xff0c;当mn时&am…...