Java多线程并发学习

news2024/11/14 19:08:55

一、Java 中用到的线程调度

1. 抢占式调度:

抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。

2. 协同式调度:

协同式调度指某一线程执行完后主动通知系统切换到另一线程上执行,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直堵塞,那么可能导致整个系统崩溃。

3. JVM 的线程调度实现(抢占式调度)

java 使用的线程调使用抢占式调度,Java中线程会按优先级分配CPU时间片运行,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。

4. 线程让出cpu的情况:

1. 当前运行线程主动放弃CPU,JVM暂时放弃CPU操作(基于时间片轮转调度的JVM操作系统不会让线程永久放弃CPU,或者说放弃本次时间片的执行权),例如调用yield()方法。
2. 当前运行线程因为某些原因进入阻塞状态,例如阻塞在I/O上。
3.当前运行线程结束,即运行完run()方法里面的任务

二、进程调度算法

1. 优先调度算法

1. 先来先服务调度算法(FCFS)
当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS 算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机,特点是:算法比较简单,可以实现基本上的公平。
2. 短作业(进程)优先调度算法
短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。该算法未照顾紧迫型作业。

2. 高优先权优先调度算法

为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF)调度算法。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程。
1. 非抢占式优先权算法
在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成;或因发生某事件使该进程放弃处理机时。这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不严的实时系统中。
2.抢占式优先权调度算法
在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。显然,这种抢占式的优先权调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。
3.高响应比优先调度算法
在批处理系统中,短作业优先算法是一种比较好的算法,其主要的不足之处是长作业的运行得不到保证。如果我们能为每个作业引入前面所述的动态优先权,并使作业的优先级随着等待时间的增加而以速率a 提高,则长作业在等待一定的时间后,必然有机会分配到处理机。该优先权的变化规律可描述为:

在这里插入图片描述

(1) 如果作业的等待时间相同,则要求服务的时间愈短,其优先权愈高,因而该算法有利于短作业。
(2) 当要求服务的时间相同时,作业的优先权决定于其等待时间,等待时间愈长,其优先权愈高,因而它实现的是先来先服务。
(3) 对于长作业,作业的优先级可以随等待时间的增加而提高,当其等待时间足够长时,其优先级便可升到很高,从而也可获得处理机。简言之,该算法既照顾了短作业,又考虑了作业到达的先后次序,不会使长作业长期得不到服务。因此,该算法实现了一种较好的折衷。当然,在利用该算法时,每要进行调度之前,都须先做响应比的计算,这会增加系统开销。

3. 基于时间片的轮转调度算法

1. 时间片轮转法
在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。
2. 多级反馈队列调度算法
(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第 1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第 1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。在多级反馈队列调度算法中,如果规定第一个队列的时间片略大于多数人机交互所需之处理时间时,便能够较好的满足各种类型用户的需要。

三、CAS(比较并交换-乐观锁机制-锁自旋)

1. 概念及特性

CAS(Compare And Swap/Set)比较并交换,CAS 算法的过程是这样:它包含 3 个参数CAS(V,E,N)。V 表示要更新的变量(内存值),E 表示预期值(旧的),N 表示新值。当且仅当 V 值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
CAS 操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。

.2. 原子包 java.util.concurrent.atomic(锁自旋)

JDK1.5 的原子包:java.util.concurrent.atomic 这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由 JVM 从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。相对于对于synchronized 这种阻塞算法,CAS 是非阻塞算法的一种常见实现。由于一般CPU切换时间比CPU指令集操作更加长,所以J.U.C在性能上有了很大的提升。

3. ABA 问题

CAS 会导致“ABA 问题”。CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出A,这时候另一个线程 two 也从内存中取出 A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version)的方式来解决ABA问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。

四、AQS(抽象的队列同步器)

AbstractQueuedSynchronizer 类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。

在这里插入图片描述
它维护了一个volatile int state(代表共享资源)和一个 FIFO 线程等待队列(多线程争用资源被阻塞时会进入此队列)。这里volatile 是核心关键词,具体 volatile 的语义,在此不述。state 的访问方式有三种:
getState()
setState()
compareAndSetState()
AQS定义两种资源共享方式
Exclusive 独占资源-ReentrantLock
Exclusive(独占,只有一个线程能执行,如ReentrantLock)
Share 共享资源-Semaphore/CountDownLatch
Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
AQS只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现,AQS这里只定义了一个接口,具体资源的获取交由自定义同步器去实现了(通过state的get/set/CAS)之所以没有定义成abstract,是因为独占模式下只用实现 tryAcquire-tryRelease,而共享模式下只用实现tryAcquireShared-tryReleaseShared。如果都定义成abstract,那么每个模式也要去实现另一模式下的接口。不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
1.isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
2.tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
3.tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
4.tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
5.tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

同步器的实现是ABS核心(state资源状态计数)

同步器的实现是ABS核心,以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用 tryAcquire()独占该锁并将 state+1。此后,其他线程再 tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取此锁的(state 会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后 countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。
ReentrantReadWriteLock 实现独占和共享两种方式
一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现 tryAcquiretryRelease、tryAcquireShared-tryReleaseShared 中的一种即可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。

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

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

相关文章

智胜未来,新时代IT技术人风口攻略-第七版(弃稿)

文章目录 前言鸿蒙生态科普调研人员画像角色先行结论 - 市场下的增量蛋糕高校助力鸿蒙 - 掀起鸿蒙教育热潮高校鸿蒙课程开设占比 - 巨大需求背后是矛盾冲突教研力量并非唯一原因 - 看重教学成果复用与效率 企业布局规划 - 多元市场前瞻视野全盘接纳仍需一段时间 - 积极正向的一…

神经网络系列---计算图基本原理

文章目录 计算图符号微分符号微分的步骤示例符号微分在计算图中的使用总结 数值微分前向差分法中心差分法数值微分的使用注意事项总结 自动微分1. 基本原理2. 主要类型3. 计算图4. 应用5. 工具和库6. 优点和缺点 计算图1. **计算图的建立**2. **前向传播**3. **反向传播**4. **…

IT廉连看——C语言——循环语句

IT廉连看——C语言——循环语句 循环语句分为三种: while for do while 一、while循环 我们已经掌握了,if语句: if(条件)语句; 当条件满足的情况下,if语句后的语句执行,否则不执行。 但是这个语句只会执行一次…

调度和管制机制

目录 1 调度机制 分组按优先级排队 公平排队 FQ (Fair Queuing) 加权公平排队 WFQ (Weighted Fair Queuing) WFQ 与 FIFO 的比较 2 管制机制 漏桶管制器 (leaky bucket policer) 3 漏桶机制与加权公平排队相结合 调度和管制机制是使互联网能够提供服务质量的重要措施。…

K线实战分析系列之三:吞没形态

K线实战分析系列之三:吞没形态 一、吞没形态二、看涨吞没形态三、看跌吞没形态四、吞没形态判别标准 一、吞没形态 两根或两根以上的K线形成的组合形态,吞没形态就是一种主要的反转形态。 这个形态由两根K线组成,前短后长,一阴一…

软考-中级-系统集成2023年综合知识(三)

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 软考中级专栏回顾 专栏…

Raspbian命令行RTSP/RTP服务

Raspbian命令行RTSP/RTP服务 1. 源由2. Raspbian摄像头2.1 命令行启动RTP摄像头2.2 命令行启动RTSP摄像头 3. 示例3.1 测试RTP摄像头3.2 测试RTSP摄像头3.3 QGroundControl测试3.3.1 RTSP配置3.3.2 RTP配置 4. 总结5. 参考资料 1. 源由 鉴于实际测试发现RTP协议下,…

K8S部署Java项目 pod报错 logs日志内容:no main manifest attribute, in app.jar

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

责任链模式与spring容器的搭配应用

背景 有个需求,原先只涉及到一种A情况设备的筛选,每次筛选会经过多个流程,比如先a功能,a功能通过再筛选b功能,然后再筛选c功能,以此类推。现在新增了另外一种B情况的筛选,B情况同样需要A情况的筛…

NestJS入门7:增加异常过滤器

前文参考: NestJS入门1 NestJS入门2:创建模块 NestJS入门3:不同请求方式前后端写法 NestJS入门4:MySQL typeorm 增删改查 NestJS入门5:加入Swagger NestJS入门6:日志中间件 本文代码基于上一篇文章《…

数字化转型导师坚鹏:政府数字化流程管理

政府数字化流程管理 课程背景: 很多政府存在以下问题: 不清楚数字化对流程有什么影响? 不知道政府业流程如何进行优化? 不知道政府业流程优化的具体案例? 课程特色: 有实战案例 有原创观点 …

【大厂AI课学习笔记NO.52】2.3深度学习开发任务实例(5)需求采集考虑维度

今天来学习,怎么做需求分析,如何明确数据采集需求。 我把自己考试通过的学习笔记,都分享到这里了,另外还有一个比较全的思维脑图,我导出为JPG文件了。下载地址在这里:https://download.csdn.net/download/g…

【C++私房菜】面向对象中的多态

文章目录 一、多态二、对象的静态类型和动态类型三、虚函数和纯虚函数1、虚函数2、虚析构函数3、抽象基类和纯虚函数4、多态的原理 四、重载、覆盖(重写)、隐藏(重定义)的对比 一、多态 OOP的核心思想是多态性(polymorphism)。多态性这个词源自希腊语,其含义是“多…

免费多域名证书,最多支持保护250个域名

随着企业规模扩大和多元化发展,拥有多个域名的需求变得普遍,此时,多域名SSL证书应运而生,并且这一类型的证书已经发展到能够安全地支持多达250个不同域名的加密需求。 多域名SSL证书,也称为SAN(Subject Alt…

【《高性能 MySQL》摘录】第 2 章 MySQL基准测试

文章目录 2.1 为什么需要基准测试2.2 基准测试的策略2.2.1 测试何种指标 2.3 基准测试方法2.3.1 设计和规划基准测试2.3.2 基准测试应该运行多长时间2.3.3 获取系统性能和状态2.3.4 获得准确的测试结果2.3.5 运行基准测试并分析结果2.3.6 绘图的重要性 2.4 基准测试工具…

[面试] 什么是死锁? 如何解决死锁?

什么是死锁 死锁,简单来说就是两个或者多个的线程在执行的过程中,争夺同一个共享资源造成的相互等待的现象。如果没有外部干预线程会一直阻塞下去. 导致死锁的原因 互斥条件,共享资源 X 和 Y 只能被一个线程占用; 请求和保持条件&#xf…

【第七天】C++模板探秘:函数模板、类模板以及类型转换的深入解析

一、模板的概述 c面向对象编程思想:封装、继承、多态 c泛型编程思想:模板 模板的分类:函数模板、类模板 函数模板(类模板):将功能相同,类型不同的函数(类)的类型抽象成虚…

Java+Vue:宠物猫认养系统的未来之路

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

网购商城系统源码 积分兑换商城系统源码 独立后台附教程

应用介绍 本文来自:网购商城系统源码 积分兑换商城系统源码 独立后台附教程 - 源码1688 简介: 网购商城系统源码 积分兑换商城系统源码 独立后台附教程 测试环境:NginxPHP7.0MySQL5.6thinkphp伪静态 图片:

进程与线程之进程的理解

首先对堆栈等进程运行过程中的内存有了更深层次的理解: 我们之前了解到,程序在运行中存在堆栈,字符串常量区代码区。 现在我们提出虚拟内存的概念:程序在运行的过程中开辟0~4G的虚拟空间使用MUU映射单元映射到物理地址上 简而言…