目录
4种拒绝策略(RejectedExecutionHandler)
什么是线程池?
线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里就是我们前面学过的线程,这里的任务就是我们前面学过的实现Runnable或Callable接口的实例对象
了解线程池之前我们需要先了解一下什么是进程和线程
进程:
进程就是正在运行的程序,它会占用对应的内存区域,由CPU进行执行与计算。独立的程序,独立的内存。进程是具有独立性、动态性(程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,具有自己的声明周期和各种不同的状态)、并发性(多个进程可以在单个处理CPU上并发执行,多个进程之间不会相互影响)
线程:
进程中的一部分,共享进程中的内存,进程是由线程构成的,一个进程可以开启多个线程,其中有一个主线程来调用本进程中的其他线程,多个线程可以让同一个进程同时并发处理多个任务,相当于扩展了进程的功能
为什么使用线程池?
使用线程池可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行压力
使用线程池有哪些优势?
1、控制和任务分离,提升线程重用性
2、控制线程并发数量,降低服务器压力,统一管理所有线程
3、提升系统响应速度,加入创建线程用的时间为T1,执行任务为T2,销毁线程为T3,那么使用线程就免去T1和T#的时间
应用场景
1、网购商品秒杀
2、云盘文件上传和下载
3、多线程批量发送短信邮件
总之:只要有并发的地方、任务数量大或小,每个任务执行时间长或短的都可以使用线程池
源码解析
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数详解:
corePoolSize | 线程核心数量 |
---|---|
maximumPoolSize | 最大线程数 |
keepAliveTime | 非核心线程存活时间 |
TimeUnit unit | 时间单位 |
BlockingQueue<Runnable> workQueue | 任务队列(阻塞队列) |
ThreadFactory threadFactory | 线程工程(创建线程的) |
RejectedExecutionHandler handler | 拒绝策略 |
线程池的工作流程
图解
1、核心线程数(corePoolSize):2
创建时,最先创建的线程是核心线程,当队列中没有task(任务)时,最后执行结束的线程是核心线程
2、最大线程数(maximumPoolSize):4
线程池中最大的线程数量
3、最大等待时间(keepAliveTime):60
非核心线程,销毁之前的等待时间
4、队列长度(workQueue):10
任务的缓存数量,当装不下的时候,需要拒绝
4个参数的设计
1、核心线程数(corePoolSize)
核心线程数的设计需要根据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,要想在1秒内处理100个任务,就需要10个线程,一般按照8020原则设计,剩下的百分之20可以利用最大线程处理。
2、最大线程数(maximumPoolSize)
最大线程数设计除了参照核心线程数条件外,还需要参照系统每秒产生的最大任务数决定。最大线程数=(最大任务数-任务队列长度)单个执行任务执行时间。例如:(100-20)0.1=8个。
3、最大等待时间(keepAliveTime)
完全参考系统运行环境和硬件压力设定,没有固定参考值
4、任务队列长度
任务队列长度一般设计为:核心线程数/单个任务执行时间*2,例如:8/0.1**2=160个
线程池的五种状态
1、RUNNING
线程池可以接收新的任务和执行已添加的任务
2、SHURDOWN
不接收新任务,但能处理已添加的任务,线程池由RUNNING -> SHUTDOWN
3、STOP
不接收新任务,不处理已添加的任务,并且会中断正在执行的任务,线程池由(RUNNING或者SHUTDOWN ) -> STOP
4、TIDYING
线程中阻塞队列为空,所有任务已终止,线程池中工作线程数量为0,线程会变为TIDYING状态
4种拒绝策略(RejectedExecutionHandler)
1、AbortPolicy
丢弃任务并抛出出RejectedExecutionException异常。
2、DiscardPolicy
丢弃任务,但是不抛出异常。
3、DiscardOldestPolicy
丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
4、CallerRunsPolicy
由调用线程处理该任务
线程池工具类
实现类:
1、Executors.newCachedThreadPool():群发短信
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
创建一个默认的线程池对象,带缓存的线程池,里面的线程可重用,且在第一次使用时才创建,核心线程数=0;最大线程数=2^31-1
2、Executors.newFixedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建一个可重用固定线程数的线程池,固定大小线程池,核心线程数=max,没有非核心线程数
3、Executors.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
4、Executors.newScheduledThreadPool()
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务;
5、Executors.newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。核心线程数1 最大线程到顶,没有停顿时间,立刻结束
总结:线程池使用步骤
1:利用Executors工厂类的静态方法,创建线程池对象;
2:编写Runnable或Callable实现类的实例对象;
3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方法提交并执行线程任务
4:如果有执行结果,则处理异步执行结果(Future)
5:调用shutdown()方法,关闭线程池