有哪些参数
- 核心线程数
有新的任务提交到线程池时,无论是否有线程空闲,就会创建线程,直到线程数达到核心线程数。 - 阻塞队列
我们最好指定固定容量的阻塞队列,防止队列无限长,导致内存溢出。
Java提供了一些默认的线程池,但是这些线程池都有这样那样的问题,其中一个就是没有给阻塞队列指定容量,可能会导致OOM。 - 最大线程数
当线程池达到了核心线程数,且所有的线程都在执行任务,且阻塞队列已满时,此时有新的任务提交,线程池就会创建额外的线程执行最新提交的任务,直到线程池的线程数达到最大线程数。 - 保活时间
当线程池的线程数超过核心线程数,且有空闲的线程时,空闲时间超过了保活时间,线程池就会被销毁。 - 线程工厂
如果自定义了线程工厂,则会使用线程工厂来创建线程池。
可以在创建线程的时候,给线程绑定异常处理策略,比如打印日志。如果不指定异常处理策略,提交的任务出了错误,抛出了异常,线程会莫名其妙的结束,且不会打印日志,也会抛出异常到提交者。
我们可以在自定的线程工厂中给线程定义名字,和业务关联起来,这样在出现现网问题时,可以根据线程打印的名字来判断是什么哪个地方的代码出了问题。 - 拒绝策略
如果线程池达到最大线程数,且阻塞队列已满,此时如果提交任务到线程池,则拒绝策略会产生作用,默认的拒绝策略是拒绝该任务,且抛出异常。
AbortPolicy(默认):拒绝当前提交的任务,且抛出异常RejectedExecutionException
CallerRunsPolicy:让提交者所在线程运行当前提交的任务,会阻塞提交任务的线程,需要谨慎使用。
DiscardPolicy:抛弃当前提交的任务。
DiscardOldestPolicy:抛弃阻塞队列队首的任务,然后将当前提交的任务加入到阻塞队列末尾。这里的Oldest表示阻塞队列中的oldest。
线程池工作流程
-
创建线程池
此时线程池中没有线程,当没有任务提交时,也不会提前创建线程 -
提交第一个任务
线程池创建第一个线程来运行提交给线程池的任务,如果定义了线程工厂,则会使用线程工厂的创建线程的方法来创建线程。 -
继续提交第二个任务
如果第一个任务运行结束,此时提交第二个任务,也会新建一个线程来执行该任务 -
继续提交任务
继续创建新的线程 -
运行的任务达到核心线程数
直到创建的线程达到核心线程数 -
继续提交任务,且阻塞队列没有满
如果所有的核心线程都在运行,继续提交任务,则会将任务暂存到阻塞队列。 -
继续提交任务,且阻塞队列已满
此时,会创建一个新线程,且会把最新的任务交给新创建的线程执行。
注:我猜这样实现,是为了实现效率最大化。这样做,可以减少一次任务进入阻塞队列及把队首的任务拿出阻塞队列。 -
运行的任务达到最大线程数
如果线程池达到最大线程数,且阻塞队列已满,此时如果提交任务到线程池,则拒绝策略会产生作用,默认的拒绝策略是拒绝该任务,且抛出异常。 -
拒绝策略
如果线程池达到最大线程数,且阻塞队列已满,此时如果提交任务到线程池,则拒绝策略会产生作用,默认的拒绝策略是拒绝该任务,且抛出异常。
基本代码
public class HelloThreadPool {
public static void main(String[] args) {
int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 120;
TimeUnit keepAliveTimeUnit = TimeUnit.SECONDS;
// 线程安全的阻塞队列
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(100);
// 创建任务
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, keepAliveTimeUnit, // 线程保活时间
blockingQueue, // 阻塞队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // AbortPolicy是默认的拒绝策略:拒绝当前请求,且抛出异常
);
// 向线程池提交一个任务
threadPoolExecutor.execute(() -> System.out.println("运行提交的任务结束"));
threadPoolExecutor.shutdown();
}
}
参考
暂无参考。