1.引子
假期的时间总是很快的,不知不觉国庆节已经过去了三天。今天利用早上的空闲,我们把高级并发编程系列的文章补一补,到这个系列的第四篇文章了,前面三篇我们分别分享了:基础概念 、核心要点 、入门案例。
在入门案例中,我们通过Executors工具类提供的辅助方法,创建了不同类型的线程池,最后在结论部分我们说:在实际项目中,不建议通过Executors工具类来创建线程池,理由是有线程溢出或者内存溢出的风险,那么在实际项目中,我们该如何正确的创建线程池呢?需要主要考虑哪些关键要素呢?你都还记得吗?比如说:
- 线程池中线程数量的控制
- 任务缓冲队列的选择
- 任务拒绝策略
这一篇让我们一起从两个角度继续深入认识线程池:juc中线程池类图、以及如何正确的创建线程池
2.案例
2.1.线程池家谱
2.1.1.类图
2.1.2.描述
在上一篇中,通过Executors工具类提供的创建线程池的辅助方法中,我们发现辅助方法内部,最终都是创建了ThreadPoolExecutor实例对象。也就是说,在juc包中,关于线程池,ThreadPoolExecutor类是主角。
同时通过idea生成的类层次图,整个线程池类图的结构主线是这样的:
-
线程池接口:Executor
- 在接口Executor中,只有一个执行任务的方方法:void execute(Runnable command)
- 我们发现execute方法的参数,是一个Runnable任务
- 该任务可以被线程池中的某个线程执行,也可以是被提交任务的线程执行
-
线程池子接口:ExecutorService
- 它是Executor的子接口,在其中增加了线程池治理的方法。比如说:shutdown (关闭线程池)、submit(获取任务执行结果方法)
-
线程池抽象类:AbstractExecutorService
- 提供了接口方法的默认共性实现
- 是模板设计模式的体现
-
线程池类:ThreadPoolExecutor
- 真正的线程池实现类
- 我们在项目中实际需要用到的线程池就是它了
2.2.创建线程池的正确姿势
2.2.1.解读ThreadPoolExecutor
从类图中,我们看到ThreadPoolExecutor是在实际项目中,需要使用到的线程池工具类。这里关于线程池的核心知识点,我们一起再进行一下回顾,便于更好的理解ThreadPoolExecutor。
关于线程池的核心知识点,你都还记得有哪些吗:
-
为什么要使用线程池
- 通过复用有限的资源,处理无限的任务,最大化合理利用有限资源的同时,提升任务处理的效率
-
如何有效的控制线程池中线程的数量
- 我们知道操作系统,对于单个进程创建线程数量是有限制的,不能无限制创建线程
- 如果我们的任务是cpu密集型 的,建议线程最大数量设置为:cup核心数的1-2倍
- 如果我们的任务是io密集型 的,建议线程最大数量设置为参考公式: (1 + 任务平均等待时间/任务处理时间) * cpu核心数
-
如果线程池中的线程都在忙,且线程数量达到了最大,此时如果有新的任务,该如何有效处理
- 在一个地方,暂时先将新任务放一放。我们称为:任务缓冲队列
- 如果线程数量达到了最大,且任务缓冲队列已经放满,需要说:no。我们需要:合理的拒绝任务策略
结合以上线程池的核心要点,我建议你去看一下ThreadPoolExecutor的源码,争取在我们使用它之前,先要更好的认识它,对吧。下面我截一个图,方便我们先直观的认识一下它:
2.2.2.案例代码
java
package com.anan.edu.common.newthread.pool.manual;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 手动创建线程池案例
*
* @author ThinkPad
* @version 1.0
* @date 2020/10/4 9:33
*/
public class ManualThreadPoolDemo {
/**
* 定义线程池参数:
*/
public static final int corePoolSize = 3;// 核心线程数量
public static final int maximumPoolSize = 5;// 最大线程数量
public static final long keepAliveTime = 60;// 空闲线程活跃时间
public static final TimeUnit timeUnit = TimeUnit.MILLISECONDS;// 时间单位
public static final BlockingQueue<Runnable> blockingQueue =
new ArrayBlockingQueue<Runnable>(100);// 任务缓冲队列
public static void main(String[] args) {
// 1.直接实例化ThreadPoolExecutor对象
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
blockingQueue);
// 2.提交任务,并执行
threadPoolExecutor.execute(new Task());
// 3.释放线程池资源
threadPoolExecutor.shutdown();
}
}
/**
* 任务
*/
class Task implements Runnable{
@Override
public void run() {
System.out.println("当前正在处理任务线程:" + Thread.currentThread().getName());
System.out.println("-----------------任务处理中---------------");
}
}
2.2.3.测试结果
vbnet
D:\02teach\01soft\jdk8\bin\java com.anan.edu.common.newthread.pool.manual.ManualThreadPoolDemo
当前正在处理任务线程:pool-1-thread-1
--------------------任务处理中------------------------
Process finished with exit code 0