目录
1. 什么是线程池
1.1 定义
1.2 工作原理
1.3 优点
1.4 举例
2. 标准库中的线程池
2.1 创建线程池
2.2 Executors 创建线程池的几种方式
2.3 ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定
3. 实现线程池
3.1 主要操作说明
3.2 代码实现
1. 什么是线程池
1.1 定义
// 线程池(Thread Pool)是一种多线程处理形式,它预先创建并管理一定数量的线程,这些线程处于等待任务的状态。当有新的任务提交时,线程池会从池中选择一个空闲线程来执行该任务;若线程池内没有空闲线程,任务会被放入等待队列,等有线程空闲时再执行;若等待队列也满了,可能会根据预设策略处理,如拒绝任务或创建新线程(在允许的最大线程数范围内)。
1.2 工作原理
// 线程创建与初始化:在创建线程池时,会根据配置参数创建一定数量的线程,这些线程在创建后进入就绪状态,等待接收任务。
// 任务提交:当有新任务到达时,线程池会将任务分配给空闲线程。如果所有线程都在忙碌,任务会被存储在任务队列中。
// 任务执行:空闲线程从任务队列中获取任务并执行。执行完成后,线程不会销毁,而是重新回到空闲状态,等待下一个任务。
// 线程管理:线程池会根据系统的负载情况和配置参数,动态地管理线程的数量。例如,当任务较多时,可能会创建新的线程;当任务较少时,会销毁一些空闲线程以节省资源。
1.3 优点
// 降低资源消耗:避免了频繁创建和销毁线程所带来的开销。创建和销毁线程需要操作系统进行一系列的操作,如分配和回收内存、更新线程调度表等,这些操作会消耗大量的系统资源。使用线程池可以复用已有的线程,减少了这些开销。
// 提高响应速度:由于线程池中的线程已经预先创建好,当有任务提交时,无需等待线程的创建过程,可立即开始执行任务,从而提高了系统的响应速度。
// 便于线程管理:线程池可以对线程进行集中管理,如设置线程的最大数量、最小数量、线程的优先级等。通过合理配置线程池的参数,可以有效地控制并发线程的数量,避免因线程过多导致系统资源耗尽或性能下降。
1.4 举例
// 线程池就相当于是在原本的基础上多创建了一个备用路径,根据任务的多少动态分配,从而提高效率和利用率.就像是餐厅的假期工,当假期中餐厅客流量大的时候已有的服务员不足以完成大量工作,为了餐厅正常运行,有的老板就会选择找假期工来度过这一段高峰期,等到客流量少的时候就可以不用招假期工,这样既可以实现餐厅增加营收,也不用像正式员工一样付出高薪,从而让自己利益最大化
2. 标准库中的线程池
2.1 创建线程池
// 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池
// 返回值类型为 ExecutorService
// 通过 ExecutorService.submit 可以注册一个任务到线程池中
// 将下列代码放入 main 函数中即可运行
java"> ExecutorService Pool = Executors.newFixedThreadPool(10);
Pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("Hello ThreadPool");
}
});
// 结果如下:
2.2 Executors 创建线程池的几种方式
2.2.1 newFixedThreadPool : 创建固定线程数的线程池
2.2.2 newCachedThreadPool : 创建线程数目动态增长的线程池
2.2.3 newSingleThreadExecutor : 创建只包含单个线程的线程池
2.2.4 newScheduledTheadPool : 设定延迟时间后执行命令, 或者定期执行命令, 是进阶版的 Timer
// Executors 本质上是 ThreadPoolExecutor (原生的,更常用)类的封装
2.3 ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定
// 以上图片为 Java 官方文档中截取
// TreadPoolExecutor 里面的线程个数并非是固定不变的,它会根据当前任务的情况动态发生变化(自适应)
// 要做到既能保证繁忙的时候可以高效完成任务,又能保证空闲时不会造成资源浪费
2.3.1 corePoolSize : 核心线程数 (正式员工的数量 [正式员工, 一经录用,永不辞退])
// 至少得有这些线程, 哪怕线程池一点任务都没有
2.3.2 maximumPoolSize : 最大线程数 (正式员工 + 临时工的数量 [临时工: 一段时间没活干就会被辞退])
// 最多只能这么多,不能超过这些线程, 哪怕再忙也不能比这个数目更多了
2.3.3 keepAliveTime : 临时工允许的空闲时间 (摸鱼时间)
2.3.4 unit : 它是时间单位, 可以是秒,分钟或其他值
2.3.5 workQueue : 传递任务的阻塞队列 (线程池内部有很多任务, 这些任务可以用阻塞队列来管理)
//线程池可以内置阻塞队列, 也可以手动指定一个
2.3.6 threadFactory : 工厂模式 (创建线程的工厂, 参与具体的线程创建工作, 通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置)
2.3.7 RejectedExecutionHandler : 拒绝策略/拒绝方式 (如果任务量超出公司的负荷了接下来怎么处理)
2.3.7.1 AbortPolicy() : 超过负荷, 直接抛出异常
2.3.7.2 CallerRunsPolicy() : 调用者负责处理多出来的任务
2.3.7.3 DiscardOldestPolicy() : 丢弃队列中最老的任务 (喜新厌旧)
2.3.7.4 DiscardPolicy() : 丢弃新来的队伍
3. 实现线程池
3.1 主要操作说明
// 核心操作位 submit, 将任务加入线程池中
// 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务
// 使用 BlockingQueue 组织所有的任务
// 每个 Worker 线程要做的事情 : 不停的从 BlockingQueue 中取任务并执行
// 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增新的线程了
3.2 代码实现
java">class MyThreadPool {
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public void submit(Runnable runnable0) throws InterruptedException {
queue.put(runnable0);
}
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
while (true) {
try {
Runnable runnable = queue.take();
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
}
// 以上是实现线程池的核心代码
java"> public static void main(String[] args) {
ExecutorService Pool = Executors.newFixedThreadPool(10);
Pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("Hello ThreadPool");
}
});
}
// 以上是主函数中运用线程池的代码