概述
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务,池化技术。
工作机制
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
使用线程池的原因:
多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。
了解线程池
如图所示,Executors 提供了四种方法创建线程
那么他的逻辑是什么:
如图所示,前篇一路的创建了一个线程池,那么可以理解他只是对我们创建线程池做了一些封装。但是这样虽然可以在工作中一部分简化了代码,但是提供了很多局限性。那么创建线程我们是可以自定义创建的。
Executor 创建有什么弊端;
如果想要了解创建线程池有什么弊端。首先我们要理解线程池创建的几个参数的意义。
我们先看一下官方给的文档:
@param corePoolSize
保留在池中的线程数,即使*处于空闲状态,除非设置了{@code allowCoreThreadTimeOut}
@param maximumPoolSize
池中允许的最大线程数@param keepAliveTime
当数量线程数大于内核数,这是多余的空闲线程在终止之前等待新任务的最长时间。@param unit {@code keepAliveTime}
参数的时间单位@param work
在执行任务之前将队列用于保存任务。此队列将仅保存由{@code execute}
方法提交的{@code Runnable}
任务。
了解参数之后我们创建线程池
默认是抛出异常的拒绝策略;
拒绝策略
在ThreadPoolExecutor 下面有几个静态类
抛出异常
运行被拒绝任务的被拒绝任务处理程序 直接拒绝让线程回到原线程执行
拒绝任务的处理程序,它丢弃最旧的未处理请求,然后重试{@code execute},除非执行程序被关闭,在这种情况下,该任务将被丢弃。 会尝试与以前久的线程进行竞争,竞争不过还是丢弃
拒绝任务的处理程序,静默丢弃*拒绝任务。 直接拒绝丢包
回答线程池创建还是自定义创建
既然知道参数还有拒绝策略
那么我们再看下创建线程的几个源码
缓冲线程最大可以开启21亿,无限次创建,无法达到容错,所以这种线程如果没有限流的情况很容易造成oom
作为单线程处理,其实没什么必要创建一个单例线程;场景较少
固定线程最大和核心线程都是一致的,不存在活跃线程和最大线程,在一定程度上,对性能还是有影响的,再看一下队列,
默认队列是21亿
的数据,所以说这种也不会出现拒绝策略
所以结论是: 我们因该自己创建线程池,进行线程维护,工具方法创建的线程,应该减少使用
关于核心线程和最大线程配置
关于这个问题,可以根据计算出当前复杂io应该承受多少线程,这个可以估算出来核心线程数,主要是最大线程数,需要关注,因为如果线程太多,会自动开启窗口。
个人有两种策略
一种是获取当前的核心线程数
Runtime.getRuntime().availableProcessors()
获取当前电脑核心数
第二种与第一种类似 Runtime.getRuntime().availableProcessors() *2
网上面算法有点复杂, 第一是 cpu
与我第一种方法类似
第二种会计算 io
复杂度,如果是正常写代码,我建议直接以第一种去 cpu
复杂度即可