【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.mfbz.cn/a/702830.html

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

相关文章

【C++ | 左值、右值】一文了解C++的左值、右值、左值引用()、右值引用()

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-12 1…

鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除

虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系&#xff0c;其中参数包含MMU结构体、解除映射的虚拟地址和解除映射的数量count,数量的单位是内存页数。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项数据。⑵处计算需要解除的无效…

python实现高斯(Gauss)迭代自动计算

实现高斯&#xff08;Gauss&#xff09;迭代自动计算 输入系数矩阵mx、值矩阵mr、迭代次数n&#xff0c;即可得到答案。本人在原博主的代码基础上优化了数据输出形式&#xff0c;原文链接&#xff1a;python实现高斯(Gauss)迭代法_python中gausspp-CSDN博客 运算结果如下图&am…

家用洗地机怎么选?四大行业精品集合,识别度超高

家用洗地机&#xff0c;作为一种能够高效清洁地面的清洁工具&#xff0c;不仅减轻了人们家务的轻度&#xff0c;也给人们腾出了很多空闲的时间去享受生活。但是洗地机那么多&#xff0c;我们在面对洗地机选购的时候&#xff0c;我们应该要注意哪些呢&#xff1f;下面就为大家详…

游戏研发(策略+sass+回调模式)

前言 由于这边需要对接游戏研发后台,基本就是开服,封禁.角色日志等,但是每个游戏提供的接口都是不一样的,所以为了统一处理提前进行sass封装,以便后续可以更好的兼容 同时还涉及了多数据源的问题,因为有些日志太大不可能直接去http调用,会使用直接查询游戏研发的数据库方式这一…

从零实现KV存储项目实战

本项目是从零实现一个完整的、兼容Redis协议的KV数据库项目。 通过每一行代码的编写。你会对整个系统了如指拿&#xff0c;这样对自己基本功的锻炼、对编程能力的提升都是很大的 项目提供完整的视频教程代码 下面是关于KV存储项目的技术大纲&#xff1a; 如果你在学习的过程…

BUAA-2024年春-OO第四单元总结

正向建模与开发 在本单元中&#xff0c;我们需要模拟一个小型的图书管理系统&#xff0c;完成图书馆所支持的相关业务&#xff0c;并遵守一定的规章制度。与前几次不同的是&#xff0c;本单元中&#xff0c;我们需要预先将自己的设计思路用UML来实现&#xff0c;然后进行编程。…

Ecovadis审核的内容

Ecovadis审核的内容。Ecovadis是一家国际性的企业社会责任评估机构&#xff0c;旨在为全球供应链的可持续性发展提供评估和审核。在本文中&#xff0c;我们将从以下几个方面详细介绍Ecovadis审核的内容&#xff1a; 一、Ecovadis审核的范围和目的 Ecovadis审核的范围涵盖了各个…

EMI电路

PFC 功率部分 1 、整流桥是串联 2 、 PFC 电感串联 3 、二极管并联 4 、 MOSFET 并联 EMI电路图

C++中的结构体——结构体嵌套结构体

作用&#xff1a;结构体中的成员可以是另一个结构体 例如&#xff1a;每一个老师辅导一个学生&#xff0c;每个老师的结构体中&#xff0c;记录一个学生的结构体 示例 运行结果

springboot二屯村钓鱼场管理系统-计算机毕业设计源码58167

摘 要 在互联网时代的来临&#xff0c;电子商务的骤起&#xff0c;一时间网络进行购物这一形式备受欢迎&#xff0c;到现在&#xff0c;网购更是普及。现如今各个行业也通过网购的方式来进行拓展业务&#xff0c;增加企业的知名度以及提升业绩&#xff0c;满足了用户像网购一样…

云原生应用开发培训,开启云计算时代的新征程

在云计算时代&#xff0c;云原生应用开发技术已经成为IT领域的热门话题。如果您想要转型至云原生领域&#xff0c;我们的云原生应用开发培训将帮助您开启新征程。 我们的课程内容涵盖了云原生技术的基础概念、容器技术、微服务架构、持续集成与持续发布&#xff08;CI/CD&#…

单细胞RNA测序(scRNA-seq) 理解Seurat对象存储信息含义和基本操作

单细胞测序技术是在单个细胞水平上&#xff0c;对基因组、转录组和表观基因组水平进行分析测序技术。bulk RNA-seq获得的是组织或器官等大量细胞中表达信号的均值&#xff0c;无法获取细胞之间的差异信息&#xff08;即丢失了细胞的异质性&#xff09;&#xff0c; 而单细胞测序…

【数组】【双指针】三数之和

打算冲一把算法类比赛&#xff0c;之前一直对算法提不起兴趣&#xff0c;也有我自己对它的抵触&#xff0c;本身算法也比较菜。 但现在打算勤勤恳恳刷题&#xff0c;踏踏实实总结&#xff0c;冲&#xff01; 数组——双指针 三数之和 该题力扣网址 错误做法 三重循环框架&a…

第十五篇——条件熵和信息增益:你提供的信息到底值多少钱?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 通过这篇文章&#xff0c;我知道了条件熵和信息增益&#xff1b;如果你试…

水电站大坝安全监测工作详解

水电站大坝安全监测是确保大坝结构安全和操作安全的关键组成部分。本文将详细解释水电站大坝安全监测的9项主要工作内容&#xff0c;帮助理解其重要性和执行过程。 1) 现场监测 现场监测是水电站大坝安全监测的首要步骤。监测人员需要定期对大坝的物理结构进行检查&#xff0c;…

vite构建的ts项目配置src别名@

一、安装types/node npm install types/node 二、vite.config.ts 文件中配置以下内容 resolve: {alias: {: path.resolve(__dirname, ./src),},}, 三、 tsconfig.json 文件中compilerOptions下配置以下内容 /* 配置 */"baseUrl": ".","paths":…

【Python】详解pandas库中pd.merge函数与代码示例

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

高考志愿填报秘籍:个人篇

选择适合自己的大学和专业&#xff0c;对广大考生来说至关重要。从某种程度上来说&#xff0c;决定了考生未来所从事的行业和发展前景。为了帮助广大考生更加科学、合理地填报志愿&#xff0c;选择适合自己的大学和专业&#xff0c;本公众号将推出如何用AI填报高考志愿专栏文章…