目录
- 初识线程池
- 为什么要引入线程池
- 标准库中的线程池
- 代码演示
- 创建线程池的时候,设置多少个线程合适??
初识线程池
池 是一个非常重要的概念
你可能听说过 池化技术
常量池、数据库连接池、线程池、进程池、内存池…
为什么要引入线程池
最开始 ,进程就能解决 并发 编程问题。
因为频繁创建销毁进程,成本太高了,因此,引入了 轻量级 进程----->线程
如果创建销毁线程的频率进一步提高,此时线程的创建销毁开销,也就不能无视了!!!!
就需要想办法优化此处的线程的创建销毁效率
解决方案,有两种:
1.引入 轻量级 线程—>也称为 纤程/协程
2.线程池,把要使用的线程提前创建好,用完了也不要直接释放而是以备下次使用,就节省了创建/销毁线程的开销
在这个使用的过程中,并没有真的 频繁创建销毁,而只是从线程池里,取线程使用,用完了还给线程池
而且从线程池里取线程,是纯用户态代码,(可控的,直接拿)
通过系统申请创建线程,就是需要内核来完成的(不太可控,你不知道什么时候给你创建好)
标准库中的线程池
ThreadPoolExecutor
这个类的构造方法,有很多参数,侧面映射出线程池的设计思路了。
参数
线程池默认提供了四种拒绝策略,分别是AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
**==
代码演示
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadDemo32 {public static void main(String[] args) {// 使用Executors工具类创建一个线程池,这里创建的是一个固定大小的线程池// newFixedThreadPool方法接受一个整数参数,表示线程池中的线程数量// 在本示例中,创建了一个包含4个线程的固定大小线程池ExecutorService service = Executors.newFixedThreadPool(4);// 向刚刚创建的线程池提交一个任务,任务以匿名内部类的形式实现了Runnable接口service.submit(new Runnable() {// 实现Runnable接口中的run方法,这个方法定义了任务具体要执行的内容@Overridepublic void run() {System.out.println("hello");}});// 注意:这里缺少了关闭线程池的操作}
}
创建线程池的时候,设置多少个线程合适??
假设一个极端情况:
如果一个进程中,所有的线程都是CPU密集型的,每个线程所有的工作都是CPU上执行的
此时,线程数目就不应该超过N(CPU逻辑核心数)
如果一个进程中,所有的线程都是IO密集型的,每个线程的大部分工作都是在等待IO,CPU消耗非常少
此时线程数目就可以很多,远远超过N
上述两个场景都是极端情况
实际上一个进程中的线程,大概率都是一部分是IO,一部分是CPU的
这里具体的比例,不好确定
综上,由于程序的复杂性,很难直接对于线程池的线程数量进行估算。
更合适的做法,----->通过实验/测试的方式找到合适的线程数目
尝试给线程池设定不同的线程数目,分别进行性能测试。
衡量每种线程数目下,总的时间开销,和系统资源占用的开销,找到这两者之间的合适的值