文章目录
- 前言
- 一、线程池是什么?
- 二、线程池的使用
- 1.代码使用线程池
- 2.剖析线程池
- 3.线程池的拒绝策略
- 三、代码模拟实现线程池
- 总结
前言
本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!
本文介绍了线程池的作用,以及线程池的重要参数以及四种拒绝策略,用代码模拟实现了线程池,本节内容理论较多,大家要细细品读。
一、线程池是什么?
线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了 复用线程、便利地管理线程和任务 并将 线程的创建 和 任务的执行 解耦开来。我们可以创建线程池来 复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗 。
线程池就是一个🧑🏻💻线程管理员,当你有新的任务需要线程去执行时,可以直接去和线程池借一个线程来用( 🔧不再频繁创建线程 ),那么这样创建线程就不是向系统申请,而是从线程池里拿,用完之后再把线程还给线程池( 💉不再随意销毁空闲线程 )。
虽然线程的创建虽然比进程更加轻量,但是频繁创建的情况下,创建线程的这笔开销也是不容忽略的!(程序员不就是让代码跑的又快又准的存在吗!😭😭😭)
二、线程池的使用
1.代码使用线程池
public static void main(String[] args) {// 创建一个固定包含10个线程的线程池ExecutorService threadPool = Executors.newFixedThreadPool(10);//像线程池里添加任务threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}
2.剖析线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
大家一定对这个创建线程池的语句有疑问。
ExecutorService翻译一下,差不多就是执行服务,也就是说这个类是用来执行服务的,用来服务线程池的。
而这种创建方式,就是 工厂模式 🏭,用来弥补构造方法的不足(参数列表相同,但是要实现的功能不同时,因此这两个功能无法同时存在,因此就用两个方法名不同的静态方法来实现),感兴趣的朋友也可以去深入了解一下🎗️。
🧑🏻💻最原装的线程池对象是 ThreadPoolExecutor
上述工厂方法是对这个对象的进一步封装。
🎉俗话说得好,万变不离其宗,所以今天就来讲解一下这个原装的线程池对象。
上图是原装线程池的四个构造方法,咱们就讲第四个构造方法,它的参数最全,覆盖了上面三个构造方法的所有参数。
1️⃣ 第一个参数:int corePoolSize,核心线程的数量。
核心线程就相当于有编制的正式员工,无论有没有任务,忙不忙,都不会把这个员工辞退,也就是说核心线程不会被销毁,他会伴随线程池的整个生命周期, corePoolSize用来规定核心线程的数量 。
2️⃣ 第二个参数:int maximumPoolSize,最大线程数。
线程数=核心线程+临时线程 ,当任务比较多,比较忙的时候,线程池就会创建一些临时线程(临时工)来帮忙,当不忙了,闲了,把临时线程销毁(辞退临时工)。 maximumPoolSize用来规定线程池中最多有多少个线程
3️⃣ 第三个参数:long keepAliveTime,临时线程的存活时间。
当不忙之后,核心线程可以忙的过来之后,不会理解销毁临时线程,而是会等待一段时间,如果这段时间又忙起来,就还接着用这些临时线程,如果这段时间都不忙那么就销毁这些临时线程, keepAliveTime用来规定不忙之后,临时线程的最大存活时间。
4️⃣第四个参数:TimeUnit unit。
规定参数keepAliveTime的单位(毫秒,秒,分…)
5️⃣第五个参数:BlockingQueue workQueue,存放任务的阻塞队列。
线程池要管理很多任务,这些任务通过这个阻塞队列来组织, 程序员可以手动给线程池指定一个队列 ,就更加便于程序员控制获取队列中的信息,submit方法就是将任务放进这个队列里。
6️⃣第六个参数:ThreadFactory threadFactory。
这是一个工厂类,与之前的工厂模式类似,就是用来辅助线程池创建线程的辅助类 。
7️⃣第七个参数:RejectedExecutionHandler handler。
线程池的拒绝策略。 如果线程池满了,仍继续往里添加任务,如何拒绝添加 。
3.线程池的拒绝策略
标准库一共为线程池提供了四种拒绝策略
大前提:任务队列已经满了,还添加任务
- 第一种策略:AbortPolicy。
直接抛出异常 。 - 第二种策略:CallerRunsPolicy。
哪个线程添加的这个任务,就 让这个线程自己去执行这个任务 。 - 第三种策略:DiscardOldestPolicy。
丢弃最老的任务 (阻塞队列中位于队首的任务) - 第四种策略:DiscardPolicy。
丢弃最新的任务 (阻塞队列中位于队尾的任务,也就是直接丢弃这个要添加的新任务)。
假设,你的任务列表容量大小为3,只能同时记住三件事,
1.第一件事:吃饭🍚
2.第二件事:打CSGO🎮
3.第三件事:敲代码🧑🏻💻
这时你的女朋友给你发微信,让你去帮她拿个快递。
四种拒绝策略分别会怎么做呢???
- 第一种策略,我让我自己嘎☠️,什么都不做了。
- 第二种策略,我记不住这第四件事了,女朋友让我去拿,她让我去,那就让她自己去拿。💢(这个任务谁添加的谁来执行)
- 第三种策略,😵丢弃吃饭(抛弃队首任务),记住帮女朋友拿快递
- 第四种策略,😵假装没看见微信,不管这个事,爱拿不拿。(抛弃队尾任务)
三、代码模拟实现线程池
public class MyThreadPool {// 阻塞队列存放任务private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();// 存放任务的方法public void subMit(Runnable runnable) throws InterruptedException {queue.put(runnable);}// 此处实现一个固定线程数的线程池public MyThreadPool (int n) {for (int i = 0; i < n; i++) {Thread thread = new Thread(()->{try {while (true) {// 此处需要让每个线程内部都有 while 循环, // 不停的从阻塞队列当中取任务,执行任务Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}}
}
总结
以上就是今天要讲的内容,本文介绍了线程池及其使用方法,更介绍了其中重要的四种拒绝策略,还用代码模拟实现了线程池而,今天的内容理论较多,代码实现比较简单,望大家慢慢品味。
路漫漫不止修身,也养性。