1. 线程池的定义
管理一组工作线程。通过线程池复用线程有以下几点优点:
-
减少资源创建 => 减少内存开销,创建线程占用内存
-
降低系统开销 => 创建线程需要时间,会延迟处理的请求
-
提高稳定稳定性 => 避免无限创建线程引起的
OutOfMemoryError
【简称OOM】
2、ThreadPoolExecutor对象
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造函数参数说明:
-
corePoolSize => 线程池核心线程数量
-
maximumPoolSize => 线程池最大数量
-
keepAliveTime => 空闲线程存活时间
-
unit => 时间单位
-
workQueue => 线程池所使用的缓冲队列
-
threadFactory => 线程池创建线程使用的工厂
-
handler => 线程池对拒绝任务的处理策略
线程池执行原理
2、线程池的四种创建方式
(1)newCachedThreadPool 创建一个可缓存的线程池,如果线程池的长度超过需要处理的,可灵活回收空闲线程,
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
阿里禁止使用Executors
去创建线程池原因
-
FixedThreadPool和SingleThreadExecutor => 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而引起
OOM
异常 -
CachedThreadPool => 允许创建的线程数为Integer.MAX_VALUE,可能会创建大量的线程,从而引起
OOM
异常
代码实践
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
try {
ExecutorService es = Executors.newCachedThreadPool();
Long i = 0L;
while (true) {
es.submit(new MyThread(++i));
}
} catch (Exception e) {
e.getCause();
e.printStackTrace();
return;
}
}
static class MyThread extends Thread{
private Long num;
public MyThread(Long num) {
this.num = num;
}
@Override
public void run() {
System.out.println("这是第=="+num+"===线程");
}
}
}
设置java虚拟机运行内存
报错信息
如何定义线程池参数
-
CPU密集型 => 线程池的大小推荐为
CPU
数量 + 1,CPU
数量可以根据Runtime.availableProcessors
方法获取 -
IO密集型 =>
CPU
数量 *CPU
利用率 * (1 + 线程等待时间/线程CPU时间) -
混合型 => 将任务分为
CPU
密集型和IO
密集型,然后分别使用不同的线程池去处理,从而使每个线程池可以根据各自的工作负载来调整 -
阻塞队列 => 推荐使用有界队列,有界队列有助于避免资源耗尽的情况发生
-
拒绝策略 => 默认采用的是
AbortPolicy
拒绝策略,直接在程序中抛出RejectedExecutionException
异常【因为是运行时异常,不强制catch
】,这种处理方式不够优雅。处理拒绝策略有以下几种比较推荐:-
在程序中捕获
RejectedExecutionException
异常,在捕获异常中对任务进行处理。针对默认拒绝策略 -
使用
CallerRunsPolicy
拒绝策略,该策略会将任务交给调用execute的线程执行【一般为主线程】,此时主线程将在一段时间内不能提交任何任务,从而使工作线程处理正在执行的任务。此时提交的线程将被保存在TCP
队列中,TCP队列满将会影响客户端,这是一种平缓的性能降低 -
自定义拒绝策略,只需要实现
RejectedExecutionHandler
接口即可 -
如果任务不是特别重要,使用
DiscardPolicy
和DiscardOldestPolicy
拒绝策略将任务丢弃也是可以
-
拒绝策略:
hreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
线程池中默认的拒绝策略:ThreadPoolExecutor.AbortPolicy。