多线程(安全 同步 线程池)

线程安全问题

  • 多线程给我们的程序带来了很大性能上的提升,但是也可能引发线程安全问题
  • 线程安全问题指的是当多个线程同时操作同一个共享资源的时候,可能会出现的操作结果不符预期问题

取钱的线程安全问题

image-20240414145604621
  • 线程安全问题出现的原因?

    • 存在多线程并发
    • 同时访问共享资源
    • 存在修改共享资源
  • 线程安全问题发生的原因是什么?

    • 多个线程同时访问同一个共享资源且存在修改该资源。

线程同步方案

认识线程同步

思想

  • 线程同步就是让多个线程实现先后依次访问共享资源,这样就解决了安全问题,它最常见的方案就是加锁
  • **加锁:**每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

同步代码块(方式一)

作用: 把访问共享资源的核心代码给上锁,以此保证线程安全。

synchronized(同步锁) { 访问共享资源的核心代码 }

同步锁的注意事项

  • 对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug
  • 对于实例方法建议使用this作为锁对象
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象

同步方法(方式二)

Lock锁(方式三)

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。

Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

构造器说明
public ReentrantLock()获得Lock锁的实现类对象

方法

方法名称说明
void lock()获得锁
void unlock()释放锁

总结(三种方式对比)

同步代码块同步方法lock
语法synchronized (this){ }synchronized 方法(){ }lock.lock(); lock.unlock();
加锁方式自动加锁、释放锁自动加锁、释放锁手动加锁、释放锁
锁粒度代码行方法代码行

线程池(重点)

认识线程池

当前创建线程的问题

  • 用户每发起一个请求,后台就需要创建一个新线程来处理,任务处理完毕之后,线程就会被销毁
  • 下次新任务来了肯定又要创建新线程处理的,用完又要被销毁
  • 而创建和销毁线程的开销是很大的,当请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能

什么是线程池?

  • 线程池就是一个可以复用线程的技术
  • 它就像一个大的池子一样,里面可以放置一些线程,当需要的时候,就从里面取出来用,用完了就还回去
  • 如此一来,就不必频繁的创建和销毁线程了,大大的提高了线程的利用率,提供系统的性能

不使用线程池的问题

  • 如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。
工作原理
image-20230508143331334

线程池的执行流程

image-20240414151501222
  • 判断核心线程数是否已满,如果没满,则创建一个新的核心线程来执行任务
  • 如果核心线程满了,则判断工作队列是否已满,如果没满,则将任务存储在这个工作队列
  • 如果工作队列满了,则判断最大线程数是否已满,如果没满,则创建临时线程执行任务
  • 如果最大线程数已满,则执行拒绝策略

如何创建线程池

谁代表线程池

JDK 5.0起提供了代表线程池的接口:ExecutorService。

如何得到线程池对象?

使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象。

public ThreadPoolExecutor( int corePoolSize,
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, 
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

参数一:corePoolSize : 指定线程池的核心线程的数量     正式工: 3  (不能小于0)                   
参数二:maximumPoolSize:指定线程池的最大线程数量	最大员工数: 5	临时工: 2  (最大数量>=核心线程数量)
参数三:keepAliveTime :指定临时线程的存活时间	(不能小于0) 	
参数四:unit:指定临时线程存活的时间单位(秒、分、时、天)	临时工空闲多久被开除   (时间单位)
参数五:workQueue:指定线程池的任务队列		客人排队的地方		(null)
参数六:threadFactory:指定线程池的线程工厂	负责招聘员工的 	(null)
参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)	忙不过来怎么办    	(null)

线程池如何处理Runnable任务

  • 使用ExecutorService的方法:
  • void execute(Runnable target)

线程池如何处理Callable任务,并得到任务执行完后返回的结果。

  • 使用ExecutorService的方法:
  • Future submit(Callable command)

Executors工具类底层是基于什么方式实现的线程池对象?

  • 线程池ExecutorService的实现类:ThreadPoolExecutor

Executors是否适合做大型互联网场景的线程池方案?

  • 不合适。
  • 建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。
任务缓冲队列
队列详解
ArrayBlockingQueue基于数组的有界缓存等待队列,可以指定缓存队列的大小
LinkedBlockingQueue基于链表的无界阻塞队列,此时最大线程数无效
任务拒绝策略
策略详解
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionException异常。是默认的策略
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy由主线程负责调用任务的run()方法从而绕过线程池直接执行

线程池处理Runnable任务

ExecutorService的常用方法

方法名称说明
void execute(Runnable command)执行 Runnable 任务
Future submit(Callable task)执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池!
List shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

线程池如何处理Runnable任务

使用ExecutorService的方法:void execute(Runnable target)

线程池处理Callable任务

ExecutorService的常用方法

方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行 Runnable 任务
Future submit(Callable task)执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
void shutdown()等任务执行完毕后关闭线程池
List shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务

线程池如何处理Callable任务,并得到任务执行完后返回的结果?

使用ExecutorService的方法:Future<T> submit(Callable<T> command)

Executors工具类实现线程池

Executors

  • **是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。 **
方法名称说明
public static ExecutorService newFixedThreadPool(int nThreads)创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
public static ExecutorService newSingleThreadExecutor()创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newCachedThreadPool()线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

注意 :这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象。

Executors使用可能存在的陷阱

  • 大型并发系统环境中使用Executors如果不注意可能会出现系统风险。
image-20240414155609462

总结

  1. Executors工具类底层是基于什么方式实现的线程池对象?
    • 线程池ExecutorService的实现类:ThreadPoolExecutor
  2. Executors是否适合做大型互联网场景的线程池方案?
    • 不合适。
    • 建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

线程通信(了解)

**什么是线程通信? **

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

线程通信的常见模型(生产者与消费者模型)

  • 生产者线程负责生产数据
  • 消费者线程负责消费生产者生产的数据。
  • 注意:生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!

线程通信的三个常见方法

方法名称说明
void wait()当前线程等待,直到另一个线程调用notify() 或 notifyAll()唤醒自己
void notify()唤醒正在等待对象监视器(锁对象)的单个线程
void notifyAll()唤醒正在等待对象监视器(锁对象)的所有线程
  • 注意
    • 上述方法应该使用当前同步锁对象进行调用。

理论补充

进程与线程

  • 进程:正在运行的程序(软件)就是一个独立的进程
  • 线程:线程是属于进程的,一个进程中可以同时运行很多个线程
  • 关系:进程=火车 线程=车厢

并发与并行

并发的含义

  • 进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发
image-20240414164954008

并行的含义

在同一个时刻上,同时有多个线程在被CPU调度执行。

image-20240414165008969

多线程到底是如何执行的?

并发与并行同时进行的

线程生命周期

也就是线程从生到死的过程中,经历的各种状态及状态转换,Java总共定义了6种状态

public class Thread{ ...  
    public enum State {
    NEW, 线程刚被创建,但是并未启动
	RUNNABLE, 线程已经调用了start(),等待CPU调度
	BLOCKED, 线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态
	WAITING, 一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒
	TIMED_WAITING, 同waiting状态,有几个方法(sleep,wait)有超时参数,调用他们将进入Timed Waiting状态
	TERMINATED; 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
	}
     ...
}

线程的6种状态互相转换

image-20240414165412488

补充知识:定时器

image-20230508143918532

image-20230508143951106

补充知识:并发、并行

image-20230508144102238

  • 简单说说并发和并行的含义
    • 并发:CPU分时轮询的执行线程。
    • 并行:同一个时刻同时在执行。

线程的生命周期(背诵)

image-20230508144234153

线程的6种状态总结

NEW(新建)线程刚被创建,但是并未启动。
Runnable(可运行)线程已经调用了start()等待CPU调度
Blocked(锁阻塞)线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态;。
Waiting(无限等待)一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒
Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

image-20230508144423987

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea创建完项目如何隐藏不重要的文件

如果您不打算直接使用这些脚本&#xff0c;而是更倾向于通过IDEA的内置工具来运行Maven命令&#xff0c;那么您可以选择隐藏这些文件。但是&#xff0c;隐藏这些文件并不会影响它们的功能&#xff0c;只是在项目视图中不再显示它们。 1.转到 File > Settings&#xff08;Wi…

时间,空间复杂度讲解——夯实根基

前言&#xff1a;本节内容属于数据结构的入门知识——算法的时间复杂度和空间复杂度。 时间复杂度和空间复杂度的知识点很少&#xff0c; 也很简单。 本节的主要篇幅会放在使用具体例题来分析时间复杂度和空间复杂度。本节内容适合刚刚接触数据结构或者基础有些薄弱的友友们哦。…

【threejs教程7】threejs聚光灯、摄影机灯和汽车运动效果

【图片完整效果代码位于文章末】 在上一篇文章中我们实现了汽车模型的加载&#xff0c;这篇文章主要讲如何让汽车看起来像在运动。同时列出聚光灯和摄像机灯光的加载方法。 查看上一篇&#x1f449;【threejs教程6】threejs加载glb模型文件&#xff08;小米su7&#xff09;&…

详解23种设计模式——单例模式

单例模式 | CoderMast编程桅杆单例模式 单例模式是最常用的设计模式之一&#xff0c;他可以保证在整个应用中&#xff0c;某个类只存在一个实例化对象&#xff0c;即全局使用到该类的只有一个对象&#xff0c;这种模式在需要限制某些类的实例数量时非常有用&#xff0c;通常全局…

The Clock and the Pizza [NeurIPS 2023 oral]

本篇文章发表于NeurIPS 2023 (oral)&#xff0c;作者来自于MIT。 文章链接&#xff1a;https://arxiv.org/abs/2306.17844 一、概述 目前&#xff0c;多模态大语言模型的出现为人工智能带来新一轮发展&#xff0c;相关理论也逐渐从纸面走向现实&#xff0c;影响着人们日常生活…

WebAssembly学习记录

1.WebAssembly 1.1 指令集 概念&#xff1a;二进制编码集合。 依据计算机组成原理和计算机概论&#xff0c;指令集是一组二进制编码。 作用&#xff1a;控制硬件。 这些二进制指令直接作用于硬件电路&#xff0c;控制硬件完成指定操作。 例如&#xff1a;控制数据进入某个寄存…

如何通过质构分析仪客观评价面包的硬度、咀嚼性等口感指标

如何通过质构分析仪客观评价面包的硬度、咀嚼性等口感指标 一、引言&#xff1a;面包口感品质的重要性 面包作为日常生活中常见的食品之一&#xff0c;其口感品质直接影响到消费者的购买决策和食用体验。其中&#xff0c;硬度和咀嚼性是衡量面包口感品质的重要指标。因此&…

车企如何利用数据技术,指导汽车全生命周期的业务运营?

引言&#xff1a;数据正作为重点&#xff0c;为行业提供不可或缺的指导 《汽车数据发展研究报告&#xff08;2023&#xff09;》指出&#xff0c;汽车行业正由传统硬件制造向“电动化、智能化、网联化”方向转变。德勤预测&#xff0c;到 2025 年&#xff0c;汽车行业 20%的利…

小程序AI智能名片S2B2C商城系统:四大主流商业模式深度解析与实战案例分享

在私域电商迅速崛起的大背景下&#xff0c;小程序AI智能名片S2B2C商城系统以其独特的商业模式和强大的功能&#xff0c;正成为品牌商们争相探索的新领域。在这一系统中&#xff0c;拼团模式、会员电商、社区团购和KOC营销等四种主流模式&#xff0c;为品牌商提供了多样化的营销…

深度探讨容器化技术在网络安全中的应用与挑战

随着容器化技术的快速发展&#xff0c;尤其是Docker与Kubernetes&#xff08;K8s&#xff09;的广泛应用&#xff0c;企业IT架构正经历着从传统虚拟机向轻量级容器的深刻变革。容器化技术为提升资源利用率、加速应用部署及维护提供了强大支持&#xff0c;但同时也给网络安全带来…

bugfix: com.alibaba.druid.sql.parser.EOFParserException: EOF

前言 在日常的开发工作中&#xff0c;我们经常会遇到各种各样的问题&#xff0c;其中涉及数据库操作的接口联调尤其容易出现意想不到的状况。今天我就遇到了一个关于Druid SQL解析异常的问题&#xff0c;具体表现为com.alibaba.druid.sql.parser.EOFParserException: EOF。通过…

42. UE5 RPG 实现火球术伤害

上一篇&#xff0c;我们解决了火球术于物体碰撞的问题&#xff0c;现在火球术能够正确的和攻击目标产生碰撞。接下来&#xff0c;我们要实现火球术的伤害功能&#xff0c;在火球术击中目标后&#xff0c;给目标造成伤害。 实现伤害功能的思路是给技能一个GameplayEffect&#x…

akSmart大带宽服务器基础配置科普

在数字化时代&#xff0c;服务器的性能和网络带宽成为业务发展的关键因素。RakSmart作为知名的服务器提供商&#xff0c;其大带宽服务器备受用户青睐。那么&#xff0c;RakSmart大带宽服务器的基础配置究竟有哪些呢?本文将为您揭开这一神秘面纱。 首先&#xff0c;我们来看看R…

阿里云X魔搭社区Create@AI创客松第四届冠军:MumuLab

4月13日终于迎来了线下Demo Day&#xff0c;此前阿里云 X 魔搭社区 X Datawhale CreateAI创客松已经紧锣密鼓地准备了一个多月时间&#xff0c;全球150团队报名、创作出66作品、评选出25支团队进入决赛&#xff0c;作品范围覆盖从办公效率到法律调解再到游戏互动以及构建童话世…

添加阿里云yum源

添加阿里云yum源 要添加阿里云的 yum 源&#xff0c;可以执行以下步骤&#xff1a; 首先&#xff0c;备份你的现有 yum 源配置文件&#xff0c;以防止意外更改&#xff1a; sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup然后&#xf…

跟着Datawhale重学数据结构与算法(3)---排序算法

开源链接&#xff1a;【 教程地址 】【电子网站】 【写博客的目的是记录自己学习过程&#xff0c;方便自己复盘&#xff0c;专业课复习】 数组排序&#xff1a; #mermaid-svg-F3iLcKsVv8gcmqqC {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16p…

【java】equals()方法

什么是Object类 Object类是所有类的始祖&#xff0c;在Java中每个类都是由它扩展而来的。 equals()方法 在 Object 类当中&#xff0c;equals()的底层是“”&#xff0c;String 类将其重写。 和equals() 有什么区别 对于基本数据类型而言&#xff0c;比较值是否相同&#x…

污水处理设备运维注意事项有哪些

污水处理设备的运维是确保污水处理效率和处理质量的关键环节。良好的运维不仅可以延长设备的使用寿命&#xff0c;还能确保污水处理过程的稳定性和可靠性。以下是一些污水处理设备运维的重要注意事项&#xff1a; 1. 定期检查和维护 设备检查&#xff1a;定期对污水处理设备进…

FFmpeg下载教程(Windows版)

文章目录 下载地址步骤 下载地址 https://ffmpeg.org/download.html 步骤

LeetCode53. 最大子数组和

LeetCode53. 最大子数组和 解题思路dp 代码 /* 数组长度n 9,连续的区间 那区间长度为1的区间数量是&#xff0c;9个 区间长度为2的区间数量是8个 区间长度为3的连续的区间数量为7个 .... 区间长度为9的区间数量为1个 */ class Solution { public:int maxSubArray(vector<…