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

线程安全问题

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

取钱的线程安全问题

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

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

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

相关文章

JAVA实现easyExcel动态生成excel

添加pom依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency><!--工具类--> <dependency><groupId>cn.hutool</groupId><…

OSPF的协议特性

路由汇总的概念 l 路由汇总&#xff08; Route Aggregation &#xff09;&#xff0c;又称路由聚合&#xff08;Route Summarization&#xff09;&#xff0c;指的是把一组明细路由汇聚成一条汇总路由条目的操作 l 路由汇总能够减少路由条目数量、减小路由表规模&#xff0…

Linux-进程和计划任务管理⭐

目录 一、程序和进程 1.程序 2.进程 3.线程与进程 二、ps查看静态进程信息 1.ps aux 命令 2.ps-静态查看系统进程 3.ps -elf 三、top-查看进程动态信息 四、pgrep查看进程信息 五、pstree-查看进程树 六、控制进程 1.进程启动方式 2.调度启动 3.进程的前后台调…

LeetCode //C - 38. Count and Say Medium Topics Companies

38. Count and Say The count-and-say sequence is a sequence of digit strings defined by the recursive formula: countAndSay(1) “1”countAndSay(n) is the way you would “say” the digit string from countAndSay(n-1), which is then converted into a differen…

Laravel 6 - 第十七章 配置数据库

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

《动手学深度学习(Pytorch版)》Task01:初识深度学习——4.22打卡

《动手学深度学习&#xff08;Pytorch版&#xff09;》Task01&#xff1a;初识深度学习 深度学习介绍AI地图深度学习任务图片分类物体检测和分割样式迁移人脸合成文字生成图片文字生成无人驾驶 案例&#xff1a;广告点击完整过程 QAQ&#xff1a;机器学习的可解释性&#xff1a…

【C++ STL序列容器】list 双向链表

文章目录 【 1. 基本原理 】【 2. list 的创建 】2.1 创建1个空的 list2.2 创建一个包含 n 个元素的 list&#xff08;默认值&#xff09;2.3 创建一个包含 n 个元素的 list&#xff08;赋初值&#xff09;2.4 通过1个 list 初始化另一个 list2.5 拷贝其他类型容器的指定元素创…

swagger文档接口根据包分组配置

文章目录 一、引言二、配置2.1 原本配置及效果2.2 更改后配置及效果 三、结束 一、引言 关于接口文档的详细配置可参见文章API文档生成工具-----Knife4j的详细介绍、配置及应用 此文章是基于已经完成基础配置的前提下,如何根据不同包进行分组 二、配置 2.1 原本配置及效果 …

Hadoop实战——MapReduce-字符统计(超详细教学,算法分析)

目录 一、前提准备工作 启动hadoop集群 二、实验过程 1.虚拟机安装先设置端口转发 2.上传对应文件 3.编写Java应用程序 4. 编译打包程序 5. 运行程序 三、算法设计和分析 算法设计 算法分析 四、实验总结 实验目的&#xff1a;给定一份英文文本&#xff0c;统计每个…

matlab新手快速上手5(蚁群算法)

本文根据一个较为简单的蚁群算法框架详细分析蚁群算法的实现过程&#xff0c;对matlab新手友好&#xff0c;源码在文末给出。 蚁群算法简介&#xff1a; 蚁群算法是一种启发式优化算法&#xff0c;灵感来源于观察蚂蚁寻找食物的行为。在这个算法中&#xff0c;解决方案被看作是…

平衡二叉树、红黑树、B树、B+树

Tree 1、前言2、平衡二叉树和红黑树3、B树和B树3.1、B树的构建3.2、B树和B树的区别3.3、数据的存储方式 1、前言 本文侧重在理论方面对平衡二叉树、红黑树、B树和B树的各方面性能进行比较。不涉及编程方面的实现。而关于于平衡二叉树在C中的实现&#xff0c;我的上一篇文章平衡…

Vue Router基础知识整理

Vue Router基础知识整理 1. 安装与使用&#xff08;Vue3&#xff09;安装使用 2. 配置路径别名和VSCode路径提示&#xff08;了解&#xff09;3. 使用查询字符串或路径传参query动态路由 与 params 4. router-link、定义别名、定义路由名称、编程式导航定义别名 aliasrouter-li…

李沐66_使用注意力机制的seq2seq——自学笔记

加入注意力 1.编码器对每次词的输出作为key和value 2.解码器RNN对上一个词的输出是query 3.注意力的输出和下一个词的词嵌入合并进入RNN 一个带有Bahdanau注意力的循环神经网络编码器-解码器模型 总结 1.seq2seq通过隐状态在编码器和解码器中传递信息 2.注意力机制可以根…

ELK技术介绍:背景、功能及应用场景全面解析

一、ELK概述 ELK是由Elasticsearch、Logstash和Kibana三个开源软件组成的日志管理解决方案&#xff0c;这一组合在近年来得到了广泛的关注和应用。ELK的出现&#xff0c;源于大数据和云计算技术的快速发展&#xff0c;以及对高效日志管理的迫切需求。 随着企业信息化程度…

【10-10-10旁观思维】项目管理必会的思维分析工具 08(送模板~)

&#x1f468;‍&#x1f4bb;&#x1f469;‍&#x1f4bb;面对一个决策或选择&#xff0c;当你犹豫不决时&#xff0c;可以想一下 ⏰10分钟后&#xff0c;自己是怎么看待自己现在的决策&#xff0c;依然保持一致亦或会后悔&#xff1b; ⏰10个月后&#xff0c;你又会如何思…

Javascript 插值搜索与二分搜索

插值搜索和二分搜索都是在有序数组中查找目标元素的算法。它们之间的核心区别在于确定中间元素的方式。 1、二分搜索&#xff08;Binary Search&#xff09;&#xff1a;二分搜索是一种通过将目标值与数组中间元素进行比较&#xff0c;然后根据比较结果缩小搜索范围的算…

Docker资源管理-数据管理

一、CPU 资源控制&#xff1a; 1.cgroups&#xff1a; cgroups&#xff0c;是一个非常强大的linux内核工具&#xff0c;他不仅可以限制被 namespace 隔离起来的资源&#xff0c; 还可以为资源设置权重、计算使用量、操控进程启停等等。 所以 cgroups&#xff08;Control grou…

深度剖析SSD掉电保护机制-1

随着固态硬盘&#xff08;Solid State Drives, SSD&#xff09;在数据中心、企业存储、个人计算设备等领域广泛应用&#xff0c;其数据安全性与可靠性成为至关重要的考量因素。其中&#xff0c;应对突发电源故障导致的数据丢失风险的掉电保护&#xff08;Power Loss Protection…

MA-Chitosan MA甲基丙烯酸修饰羧甲基壳聚糖 MA-Chitosan

MA-Chitosan MA甲基丙烯酸修饰羧甲基壳聚糖 MA-Chitosan、 【中文名称】甲基丙烯酸化羧甲基壳聚糖 【英文名称】Chitosan-MA 【结 构】 【纯 度】95%以上 【保 存】-20℃ 【规 格】10mg,500mg,1g,5g,10g 【产品特性】 Chitosan-MA&#xff08;壳聚糖-甲基丙烯酸…

Verilog基础语法——parameter、localparam与`define

Verilog基础语法——parameter、localparam与define 写在前面一、localparam二、parameter三、define写在最后 写在前面 在使用Verilog编写RTL代码时&#xff0c;如果需要定义一个常量&#xff0c;可以使用define、parameter和localparam三种进行定义与赋值。 一、localparam …