行业知识
Java——线程池详细讲解
文章目录
- 一、线程池
- 一、线程池基础
- 1.1 什么是线程池
- 1.2 为什么使用线程池
- 1.3 线程池有哪些优势
- 1.4 应用场景
- 二、线程池使用
- 2.1 Java内置线程池 ThreadPoolExecutor
- 2.1.1 线程池的七个参数
- 2.1.1.1 **int corePoolSize 核心线程数量**
- 2.1.1.2 int maximumPoolSize 最大线程数
- 2.1.1.3 long keepAliveTime 最大空闲时间
- 2.1.1.4 TimeUnit unit 时间单位
- 2.1.1.5 BlockingQueue workQueue 任务队列
- 2.1.1.6 ThreadFactory threadFactory 线程工厂
- 2.1.1.7 RejectedExecutionHandler handler 饱和处理机制
- 2.2 线程池工作流程介绍
- 三、自定义线线程池
- 3.1 参数设计分析
- 3.1.1 核心线程数量 corePoolSize
- 3.1.2 任务队列长度 workQueue
- 3.1.3 最大线程数 maximumPoolSize
- 3.1.4 最大空闲时间 KeepAliveTime
- 3.2 自定义线程池实现步骤
- 3.2.1 编写任务类(MyTask) 实现Runnable接口
- 3.2.2 编写线程类(MyWork) 用于执行任务 需要持有所有任务
- 3.2.3 编写线程池类(MyThreadPool) 包含提交任务,执行任务能力
- 3.2.4 编写测试类 创建线程池对象,提交多个任务测试
- 四、 Java内置线程池 - ExecutorService 介绍
- 4.1 常用方法
- 4.2 ExecutorService 获取
- 4.2.1 static ExecutorService newCachedThreadPool ( )
- 4.2.2 static ExecutorService newCachedThreadPool (ThreadFactory threadFactory)
- 4.2.3 static ExecutorService newFixedThreadPool(int nThreads)
- 4.2.4 static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
- 4.2.5 static ExecutorService newSingleThreadExecutor()
- 4.2.6 static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
- 五、Java内置线程池 - ScheduledExecutorService
- 5.1 ScheduledExecutorService 获取
- 5.1.1 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
- 5.1.2 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
- 5.1.3 static ScheduledExecutorService newSingleThreadScheduledExecutor()
- 5.1.4 static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
- 5.2 ScheduledExecutorService 常用方法
- 5.2.1 ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit)
- 5.2.2 ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit)
- 5.2.3 ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
- 5.2.4 ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
- 六、 Future - 异步计算结果
- 6.1 常用方法
- 6.2 Future演示
- 七、 综合案例
- 7.1 案例
- 7.2 代码
一、线程池
线程池是一种常见的多线程编程技术,它可以在执行任务时复用已创建的多个线程,并且可以控制同时运行的线程数以避免资源占用过多的问题。下面是一个简单的Java示例代码,演示如何使用线程池。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); // 创建一个容量为 5 的线程池 for (int i = 0; i 上述代码定义了一个包含10个任务的线程池(容量为5),每个任务被分配到池中可用的空闲线程,当线程完成任务后,它会返回线程池以便其他任务使用。当所有任务完成时,调用executorService.shutdown()方法关闭线程池。
总之,在编写多线程应用程序时,利用线程池可以更有效地管理资源和提高系统性能。同时,由于在线程池中的线程具有重复使用性、复用性和恰当的个数等优点,因此建议在需要频繁创建并执行线程的情况下,尽可能使用线程池来实现。
一、线程池基础
1.1 什么是线程池
线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。
这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象
1.2 为什么使用线程池
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;
1.3 线程池有哪些优势
- 线程和任务分离,提升线程重用性
- 控制线程并发数量,降低服务器压力,统一管理所有线程
- 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么线程池就免去了T1和T3的时间
1.4 应用场景
应用场景介绍:
- 网购商品秒杀
- 云盘文件上传和下载
- 12306网上购票系统
只要有并发的地方、任何数量大或小、每个任务执行时间长活短都可以使用线程池
只不过在使用线程池的时候,注意一下设置合理的线程池大小即可
二、线程池使用
2.1 Java内置线程池 ThreadPoolExecutor
2.1.1 线程池的七个参数
2.1.1.1 int corePoolSize 核心线程数量
线程池中的核心线程数量指的是线程池在正常情况下需要保持的最小线程数量,当我们执行任务时线程线程数量没有达到核心线程数量,那就会新开线程。
当有任务提交到线程池时,线程池会先尝试通过核心线程来处理任务。只有当核心线程都被占用,并且任务队列已满时,线程池才会创建新的线程。因此,核心线程数量的大小可以直接影响线程池的性能和资源消耗。如果核心线程数量设置过大,可能会浪费系统资源;如果设置过小,可能会导致任务等待时间增加,进而降低系统的响应速度。在实际应用中,应根据具体的业务场景和系统负载情况来合理设置线程池的核心线程数量。
2.1.1.2 int maximumPoolSize 最大线程数
线程池中的最大线程数量指的是线程池允许创建的最大线程数,包括核心线程和非核心线程。
当任务队列已满,并且当前线程数小于最大线程数时,线程池会创建新的线程来处理任务。但是设置过大的最大线程数可能会带来一些负面影响,如增加系统资源消耗和降低系统的稳定性。因此,在合理使用线程池进行任务调度的前提下,需要根据业务场景和系统负载情况合理设置线程池的最大线程数量。
2.1.1.3 long keepAliveTime 最大空闲时间
也可以叫做存活时间。
当我们的一个线程不用的时候允许空闲,但是空闲到一定时间之后线程池也会回收线程。
线程池中的最大空闲时间指的是一个非核心线程在空闲状态下保持存活的最长时间。
当线程池中的非核心线程数量超过了核心线程数量时,超过核心线程数量的这些线程被称为“非核心线程”。
这些非核心线程在处理完任务后并不会立即销毁,而是处于等待下一次任务的空闲状态。线程池中的最大空闲时间设置了一个阈值,若一个非核心线程在空闲状态下超过了该时间限制,线程池就会将其销毁,以释放系统资源。
合理设置线程池的最大空闲时间可以有效避免因长时间运行的线程造成的资源浪费和系统负载过高的问题。但是如果将最大空闲时间设置得太短,则可能导致过多的线程被销毁和创建,反而会影响性能。正确地设置线程池的参数需要综合考虑业务场景和系统负载情况,并通过实验和观测对其进行调优
2.1.1.4 TimeUnit unit 时间单位
枚举类
2.1.1.5 BlockingQueue workQueue 任务队列
作用:暂存尚未执行的任务的数据结构(临时缓冲区)
当我们的线程数量达到核心线程数量后,如果再有任务提交到线程池里面,此时不会里面创建新线程,而是将任务添加到任务队列里面去,只有当任务队列加满后,按照我们设置的最大线程数来逐步创建线程(创建的线程一定不大于最大线程数)
2.1.1.6 ThreadFactory threadFactory 线程工厂
允许我们自己参与创建线程的过程
class SimpleThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { return new Thread(r); } }2.1.1.7 RejectedExecutionHandler handler 饱和处理机制
线程数到达核心线程数量,并且任务队列满了,线程数也到达最大线程数了, 也就是说线程池处于饱和状态了,已经无法再融入其他任务了,那这个时候我们就能给出一个饱和处理机制。
比如让那些任务等一会啊,抛弃一些任务啊
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝执行处理程序。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory)
-
创建一个新 ThreadPoolExecutor给定的初始参数。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
2.2 线程池工作流程介绍
三、自定义线线程池
3.1 参数设计分析
3.1.1 核心线程数量 corePoolSize
核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定
例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理
3.1.2 任务队列长度 workQueue
任务队列长度一般设计为:核心线程数/单个任务执行时间成×2即可
例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200
3.1.3 最大线程数 maximumPoolSize
最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定
例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,
最大线程数=(最大任务数-任务队列长度)×单个任务执行时间 : 最大线程数=(1000-200)×0.1=80
3.1.4 最大空闲时间 KeepAliveTime
这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值。
用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可。
3.2 自定义线程池实现步骤
3.2.1 编写任务类(MyTask) 实现Runnable接口
/** * 要求: * 自定义线程池练习,这是任务类,需要实现接口 * 包含任务编号,每一个任务执行时间设计为0.2秒 */public class MyTask implements Runnable { private int id ; // 任务编号// 由于run方法是重写接口中的方法,不可添加形式参数,但是我们可以通过构造方法添加 @Override public void run() { String name = Thread.currentThread().getName(); System.out.println("线程:"+name+"即将执行任务:"+id); try { Thread.sleep(200); //休眠200毫秒 } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("线程:"+name+"完成了任务:"+id); } public MyTask(int id) { this.id = id; } @Override public String toString() { return "MyTask{" + "brush:python;toolbar:false">/** * 需求: * 编写一个线程类,需要去继承Thread,设计一个属性,用于保存线程的名字 * 设计一个集合,用于保存所有的任务 */public class MyWorker extends Thread{ private String name; //保存线程名字 private List tasks; //保存将来所有的任务 /** * 判断集合中是否有任务,只要有任务,就一直执行 */ @Override public void run() { while (tasks.size()>0){// 将第一个任务移除出来 Runnable runnable = tasks.remove(0);// 一直执行任务 runnable.run(); } }// 利用构造方法给成员变量赋值 public MyWorker(String name, List tasks) { super(name);// this.name = name; this.tasks = tasks; }}3.2.3 编写线程池类(MyThreadPool) 包含提交任务,执行任务能力
/** * 自定义的线程池类 * 成员变量: * 1. 任务队列 集合来表示即可(线程安全的集合) * 2. 当前线程数量 * 3. 核心线程数量 * 4. 最大线程数 * 5. 任务队列长度 * 没有设置最大空闲时间 * * 成员方法: * 1.提交任务:将任务添加到集合中(如果没有超出任务队列的长度则可以加入到任务队列中) * 2.执行任务: 判断当前线程的数量,决定创建核心线程(如果当前线程数量在核心线程数量之下)还是非核心线程(当前线程数量在核心线程数量与最大线程数量之间) */public class MyThreadPool {// 1. 任务队列 集合来表示即可(线程安全的集合)// LinkedList集合是非线程安全的,我们可以使用集合工具类中方法将其转变为线程安全的 private List tasks = Collections.synchronizedList(new LinkedList());// 2. 当前线程数量,初始时是0 private int num;// 3. 核心线程数量 private int corePoolSize;// 4. 最大线程数 private int maxSize;// 5. 任务队列长度 private int workSize;// 1.提交任务: public void submit(Runnable runnable){// 判断当前集合中任务的数量,是否超出了最大任务数量 if (tasks.size()>=workSize){// TODO 简陋的饱和机制处理 System.out.println("任务:"+runnable+"被丢弃了"); }else { tasks.add(runnable);// TODO 执行任务 execTask(runnable); //这个地方最好传入线程,不传入任务 } }// 2.执行任务: private void execTask(Runnable runnable) {// TODO 判断当前线程池中的线程总数量,是否超出了核心数 if(num MyWorker myWorker = new MyWorker("核心线程:"+num,tasks);// Thread t = new Thread(myWorker);// 启动线程 myWorker.start(); num++; }else if (num
-
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。
- 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝执行处理程序。