【Java面试】十九、并发篇(下):线程池

文章目录

  • 1、为什么要使用线程池
  • 2、线程池的执行原理
    • 2.1 七个核心参数
    • 2.2 线程池的执行原理
  • 3、线程池用到的常见的阻塞队列有哪些
  • 4、如何确定核心线程数开多少个?
  • 5、线程池的种类有哪些?
  • 6、为什么不建议用Executors封装好的静态方法创建线程池
  • 7、线程池的使用场景
  • 8、如何控制某个方法运行并发访问线程的数量
  • 9、ThreadLocal相关
    • 9.1 理解
    • 9.2 ThreadLocal的内存泄露问题

1、为什么要使用线程池

  • 降低资源消耗:降低避免频繁创建和销毁线程的代价
  • 提高响应速度:任务达到时,不用再等待创建线程
  • 线程管理方便:线程过多,调度开销大,用线程池可防止过分调度,且可以做统一的监控、分配、调优

此外,还有:

  • 每次创建线程,都要占用一定的内存空间,如果无限制的创建线程,会浪费内存
  • 一核的CPU,同一时刻只能处理一个线程,如果大量请求一来就创建对应数量的线程,那很多线程也没有CPU时间片,只能阻塞,还会导致线程之间频繁切换

2、线程池的执行原理

2.1 七个核心参数

以银行为例对比:银行大厅一共有10个窗口(最大线程数量),但平时一般只开5个(常驻线程数量),某天办理业务的人很多,5个窗口不够用,其余人来了就先在大厅椅子上坐着等(阻塞队列),结果椅子坐满了,还有人陆续来,于是10个窗口全开,还来很多人,那就只能告诉新来的今天轮不到你办了(拒绝策略)。

在这里插入图片描述

解释:

  • corePoolSize:核心线程数目
  • maximumPoolSize:最大线程数目 = 核心线程+ 救急线程的最大数目
  • keepAliveTime:救急线程的生存时间,没有活跃的任务给救急线程处理了,超过了生存时间就会释放
  • unit:救急线程的生存时间单位
  • workQueue:当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务(阻塞队列)
  • threadFactory:线程工厂,定制线程对象的创建,如设置线程名字、是否是守护线程
  • handler:拒绝策略 ,当所有线程都在繁忙, workQueue 也放满时,会触发拒绝策略

2.2 线程池的执行原理

提交一个任务到线程池以后:先判断核心线程数是否满了,否则直接让核心线程去执行,反之继续判断阻塞队列是否已满,没满就扔阻塞队列,满了就看线程数是否超过总数,没超,说明还有应急线程可用,反之则走拒绝策略:

  • AbortPolicy:直接抛出异常,默认策略
  • CallerRunsPolicy:用调用者所在的线程来执行任务,如main线程
  • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务
  • DiscardPolicy:直接丢弃当前任务,不抛异常

注意,救急线程(或者叫临时线程、非核心线程)执行完手里的任务后,会检查阻塞队列中是否有需要执行的线程,有则继续干。对于核心线程,正式工,它们一直存在,自然更要检查阻塞队列,然后继续干队列里的活儿。

在这里插入图片描述

3、线程池用到的常见的阻塞队列有哪些

【阻塞队列】

当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务,常见的有:

  • ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO
  • LinkedBlockingQueue:基于链表结构的有界阻塞队列(长度默认Int的最大值),FIFO
  • DelayedWorkQueue :是一个优先级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前
  • SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作

LinkedBlockingQueue最为常用:

在这里插入图片描述

关于两把锁和一把锁:每个提交的任务封装成一个Node对象放进阻塞队列时,LinkedBlockingQueue 使用了两个锁,一个是 takeLock 用于控制出队操作,另一个是 putLock 用于控制入队操作。ArrayBlockingQueue则出队和入队都是同一把锁。

在这里插入图片描述

最后,PS:

线程池的阻塞队列中存储的是任务对象(实现了 Runnable 或 Callable 接口的实例),线程池中的工作线程不断从阻塞队列中取出任务并执行

4、如何确定核心线程数开多少个?

假设N为CPU的核心数。

1)如果场景是高并发且任务执行时间短,核心线程数设置为N+ 1,减少线程上下文切换

2)如果是并发不高,且执行任务时间长,则:

  • 对于IO密集型任务,如文件读写、DB读写、网络请求,核心线程数设置为2N + 1
  • 对于CPU密集型任务,如计算型代码、Bitmap转换,核心线程数设置为N+ 1

因为对于IO密集的任务,其不耗费CPU,而偏计算型的任务(CPU密集型任务),设置为N + 1可以避免频繁切换CPU

在这里插入图片描述

最后,如果是并发高且每个业务执行时间也长,那这是优化重点就不是线程池了,而是整体架构,比如是否加入缓存,是否增加服务器,再看核心数是N+1 还是 2N+1

5、线程池的种类有哪些?

java.util.concurrent.Executors中提供了大量创建线程池的静态方法,常见的有:

1)固定线程数的线程池

在这里插入图片描述

  • 核心线程数与最大线程数相等,没有救急线程
  • 阻塞队列是 LinkedBlockingQueue,最大容量为int最大值

适用于任务量已知,相对耗时的任务

2)单线程的线程池

在这里插入图片描述

  • 核心线程数与最大线程数都是1,没有救急线程
  • 阻塞队列是 LinkedBlockingQueue,最大容量为int的最大值

只有一个线程,后面的请求过来,对应的线程进入阻塞队列,因此可以保证所有任务按顺序执行

3)可缓存的线程池

若线程池长度超过了处理需要,则灵活回收空闲线程,反之,则新建线程
在这里插入图片描述

  • 核心线程数为 0
  • 最大线程数为int的最大值
  • 阻塞队列为 SynchronousQueue,是一种不存储元素的阻塞队列,一个线程写入了数据,就必须得有一个线程取,否则不能再继续添加,用于传递性的场景

适合任务数比较密集,但每个任务执行时间较短的情况,否则会创建出大量线程

4)可执行延迟任务的线程池

源码:

在这里插入图片描述

如下,提交三个任务,分别延时0、1、5秒后可以从延迟队列中取到这个任务,然后从线程池分配个线程去执行

在这里插入图片描述

6、为什么不建议用Executors封装好的静态方法创建线程池

在这里插入图片描述

7、线程池的使用场景

单个任务处理时间比较短,但需要处理的任务的数量大。具体场景有:

  • 批量导入:如MySQL同步到ES。线程池 + CountDownLatch,分批导入Especially,防止OOM
  • 数据汇总:如资产全景、报表展示。调用多个接口汇总数据,且这些接口之间没有依赖关系,可用线程池 + future提高性能
  • 异步:如保存搜索记录,异步线程调用下一个方法,不影响上一级方法的性能

【以上三个使用场景的代码实现】

8、如何控制某个方法运行并发访问线程的数量

【JUC辅助类】

使用JUC辅助类Semaphore,维护一定数量的信号量,底层为AQS。常用于实现限流。

在这里插入图片描述

public class SemaphoreDemo {public static void main(String[] args) {//创建Semaphore,设置许可数量,三个车位,对应三个许可证Semaphore semaphore = new Semaphore(3);//模拟6辆汽车for (int i = 1; i <= 6; i++) {new Thread(() -> {try {//抢占许可证semaphore.acquire();System.out.println(Thread.currentThread().getName() + "抢到了车位");//设置一个5s以内的随机时间,模拟停车TimeUnit.SECONDS.sleep(new Random().nextInt(5));System.out.println(Thread.currentThread().getName() + "=====> 离开了车位");} catch (InterruptedException e) {e.printStackTrace();} finally {//释放许可semaphore.release();}},String.valueOf(i)).start();}}
}

在这里插入图片描述

和对象锁类似,不同的是,一个对象一把锁,而Semaphore可以自己指定信号量,一个信号量类似一把锁。

9、ThreadLocal相关

9.1 理解

成员变量,本来有线程安全问题,用ThreadLocal包装一下,则可实现每个线程都有自己的独立副本。 如用JDBC操作数据库时,会将各自的Connection对象用ThreadLocal包装,从而保证每个线程都在自己的Connection上操作数据库

在这里插入图片描述

ThreadLocal让每个线程只操作自己内部的值,从而实现线程数据隔离。ThreadLocal的结构如下,其有个内部类ThreadLocalMap,而ThreadLocalMap中有个table属性,是一个数组,数组里存着一个个的Entry对象。

在这里插入图片描述

而每个线程对象,又有ThreadLocal.ThreadLocalMap类型的属性,即每个线程对象,都有一个ThreadLocalMap对象。调用ThreadLocal的set、get、remove时,都是操作的当前线程的ThreadLocalMap的Entry类型的数组,这也是ThreadLocal对象实现线程隔离的关键,普通对象的set、get改的是普通对象自己,而ThreadLocal对象set、get改的当前线程对象的属性。

在这里插入图片描述

Thread、ThreadLocal、ThreadLocalMap的Entry数组,分别就像人、人的各种卡片(如身份证、学生证)、存各种卡片的卡包。每个人都有一个自己的卡包,卡包里装的卡片外形都一样(类比ThreadLcoal类型的成员变量),但卡片上面记录的信息是私有的,每个人的都不同(类比每个线程给ThreadLcoal类型的成员变量赋的值都不同)。

在这里插入图片描述
【ThreadLocal】

9.2 ThreadLocal的内存泄露问题

ThreadLocalMap 中的 key是弱引用,value 为强引用,key会被GC释放内存,关联 value的内存并不会释放,建议主动remove 释放 key,value

在这里插入图片描述

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

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

相关文章

C++青少年简明教程:C++的指针入门

C青少年简明教程&#xff1a;C的指针入门 说到指针&#xff0c;就不可能脱离开内存。了解C的指针对于初学者来说可能有些复杂&#xff0c;我们可以试着以一种简单、形象且易于理解的方式来解释&#xff1a; 首先&#xff0c;我们可以将计算机内存想象成一个巨大的有许多格子的…

快速开发的UI框架:效率蹭蹭提高!!【送源码】

不知道各位用uniapp 开发移动端小程序或者网页&#xff0c;是否用UI框架。 我一般就用官方自带的&#xff0c;近期一个项目 用了uView, 感觉整体还不错&#xff0c;类似蚂蚁的风格。 特此推荐下&#xff0c;可以收藏一下&#xff0c;需要的时候记得来取哦&#xff01; 介绍 …

Linux 线程控制

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 前言 1.线程现象 2.线程等待 3.线程…

git配置2-不同的代码托管平台配置不同的ssh key

1. 配置单个ssh key 1.1. 原理1.2. 生成 ssh key1.3. 代码托管平台配置公钥 2. 配置多个ssh key 2.1. 应用场景2.2. 生成两个不同的key2.3. 修改config文件2.4. 配置代码托管平台2.5. 测试是否成功 1. 配置单个ssh key 1.1. 原理 使用ssh命令行工具&#xff08;git安装成功…

【APP移动端自动化测试】第四节.元素操作的API

文章目录 前言一、点击&输入&清空操作 1.1 点击元素 1.2 输入&清空元素二、获取文本内容&位置&大小操作 2.1 获取文本内容 2.2 获取位置&大小三、根据属性名获取属性值操作四、滑动和拖拽操作 4.1 _swipe 4.2 _scroll …

博瓦科技产品亮相湖北安博会啦!!!

6月12日&#xff0c;第二十三届2024中国&#xff08;武汉&#xff09;社会公共安全产品暨数字城市产业展览会&#xff08;简称&#xff1a;湖北安博会&#xff09;在武汉国际会展中心隆重开幕。作为行业内最具影响力的展会之一&#xff0c;此次盛会将汇聚来自全球的顶尖企业、专…

Github 2024-06-08 开源项目日报Top10

根据Github Trendings的统计,今日(2024-06-08统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目4Python项目2Swift项目1Svelte项目1Jupyter Notebook项目1非开发语言项目1Go项目1Vue项目1Lua项目1Visual Studio Code - 开源项…

G6 - CycleGAN实战

&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/) 目录 理论知识CycleGAN能做什么 模型结构损失函数 模型效果总结与…

Vue小记——如何理解 $nextTick( ) ?

1、如何理解 $nextTick() 是一个非常重要的方法&#xff0c;用于在数据变动后&#xff0c;但DOM更新完成之前&#xff0c;执行特定的回调函数。这个方法确保了代码在DOM元素实际被更新之后才运行。 这是用来处理Vue的异步更新队列的。在Vue中&#xff0c;当你改变数据时&#…

OSINT技术情报精选·2024年6月第1周

OSINT技术情报精选2024年6月第1周 2024.6.11版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、经合组织&#xff1a;《2024数字经济展望&#xff1a;第1卷,拥抱技术前沿》 经合组织近日发布《2024数字经济展望》报告第一卷&#xff0c;…

每天五分钟深度学习:逻辑回归算法完成m个样本的梯度下降

本文重点 上节课程我们学习了单样本逻辑回归算法的梯度下降,实际使用中我们肯定是m个样本的梯度下降,那么m个样本的如何完成梯度下降呢? m个样本的损失函数定义为: 我们定义第i个样本的dw、db为: dw和db为损失J对w和b的偏导数,因为m个样本的代价函数J是1到m个样本总损失…

适合各行各业在线预约的自定义小程序源码系统 前后端分离 带完整的安装代码包以及代搭建教程

系统概述 这款自定义小程序源码系统是为了适应不同行业的预约需求而设计的。它具有高度的灵活性和可扩展性&#xff0c;可以根据不同用户的需求进行定制化开发&#xff0c;满足各种复杂的业务场景。 系统的前端采用了先进的小程序技术&#xff0c;为用户提供了简洁、直观的操…

【Windows】Topaz Gigapixel AI(人工智能图片放大工具)软件介绍和安装教程

软件介绍 Topaz Gigapixel AI是一款由Topaz Labs开发的先进图像放大软件&#xff0c;利用人工智能&#xff08;AI&#xff09;技术来放大图像&#xff0c;同时保持或甚至增强图像的细节和清晰度。这款软件特别适用于需要高质量图像放大的摄影师、设计师以及其他视觉内容创作者…

四六级考前突击之同义词替换

最常见的十组同义词替换 新的 Novel-new-fresh-most recent 奇怪的 Strange-odd-unusual-uncommon-Unfamiliar-peculiar odds之后的odds变成了机会的意思,同义词有chance-opportunity 传统 Convention-tradition convention还有大会和集会的意思 形容词为Conventional…

图片based64编码解码python代码

图片based64编码解码python代码 import base64 from PIL import Imagedef image_to_base64(image_path):# 打开图片文件image Image.open(image_path)# 将图片转换为二进制数据image_bytes Nonewith open(image_path, rb) as image_file:image_bytes image_file.read()# 将二…

【C++】模板及模板的特化

目录 一&#xff0c;模板 1&#xff0c;函数模板 什么是函数模板 函数模板原理 函数模板的实例化 推演(隐式)实例化 显示实例化 模板的参数的匹配原则 2&#xff0c;类模板 什么是类模板 类模板的实例化 二&#xff0c;模板的特化 1&#xff0c;类模板的特化 全特化…

dvc 更新加载模型版本控制

dvc即data version control, 是一种针对人工智能项目&#xff08;机器学习或者深度学习&#xff09;的数据版本管理工具。DVC的操作和GIT类似&#xff0c;可以认为为GIT的二次开发封装。结合GIT&#xff0c;DVC可以有效的管理人工智能项目的整个流程&#xff0c;包括代码&#…

YOLOv10在RK3588上的测试(进行中...)

1.代码源 国内镜像站在gitcode。这个镜像站也基本上包含了github上常用项目的镜像。然后它的主发布源在这里&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 yolov10是清华主导做的... 然后&#xff0c;在维护列表里看到了这个&#xff1a; 2024年05月31日&am…

并 查 集 模 板

朴素并查集&#xff1a; int p[N]; //存储每个点的祖宗节点// 返回x的祖宗节点int find(int x){if (p[x] ! x) p[x] find(p[x]);return p[x];}// 初始化&#xff0c;假定节点编号是1~nfor (int i 1; i < n; i ) p[i] i;// 合并a和b所在的两个集合&#xff1a;p[find(a)]…

【React】配置别名路径@

别名路径配置 1. 路径解析配置&#xff08;webpack&#xff09; CRA本身把webpack配置包装到了黑盒里无法直接修改&#xff0c;需要借助一个插件 - craco步骤 安装craco npm i -D craco/craco项目根目录下创建配置文件 craco.config.js配置文件中添加路径解析配置 const pa…