线程池之ThreadPoolExecutor概述
ThreadPoolExecutor提供了四个构造方法:
我们以最后一个构造方法(参数最多的那个),对其参数进行解释:
public ThreadPoolExecutor( int corePoolSize, // 1 int maximumPoolSize, // 2 long keepAliveTime, // 3 TimeUnit unit, // 4 BlockingQueue<Runnable> workQueue, // 5 ThreadFactory threadFactory, // 6 RejectedExecutionHandler handler ) { //7 if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0 ) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null ) throw new NullPointerException(); this .corePoolSize = corePoolSize; this .maximumPoolSize = maximumPoolSize; this .workQueue = workQueue; this .keepAliveTime = unit.toNanos(keepAliveTime); this .threadFactory = threadFactory; this .handler = handler; }
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue<Runnable> | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
自定义线程池:
public class ThreadPoolTest { public static void main(String[] args) throws IOException { int corePoolSize = 2 ; // 核心线程大小 int maximumPoolSize = 4 ; // 最大线程池大小 long keepAliveTime = 10 ; // 线程空闲10s后自动结束 TimeUnit unit = TimeUnit.SECONDS; // ArrayBlockingQueue,该阻塞队列底层维护了一个定长数组(创建对象必须指定容量) BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>( 2 ); ThreadFactory threadFactory = new NameTreadFactory(); // 自定义线程工厂 RejectedExecutionHandler handler = new MyIgnorePolicy(); // 自定义拒绝策略 // handler = new ThreadPoolExecutor.AbortPolicy();// 无法处理新任务会抛出RejectedExecutionException异常 // 创建线程池对象 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); executor.prestartAllCoreThreads(); // 预启动所有核心线程 // 创建10个任务对象提交到线程池中 for ( int i = 1 ; i <= 10 ; i++) { MyTask task = new MyTask(String.valueOf(i)); executor.execute(task); } System.in.read(); //阻塞主线程 } // 任务对象 static class MyTask implements Runnable { private String name; public MyTask(String name) { this .name = name; } @Override public void run() { try { System.out.println( this .toString() + " is running!" ); Thread.sleep( 3000 ); //让任务执行慢点 } catch (InterruptedException e) { e.printStackTrace(); } } public String getName() { return name; } @Override public String toString() { return "MyTask [name=" + name + "]" ; } } // 自定义拒绝策略 static class MyIgnorePolicy implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { doLog(r, executor); } private void doLog(Runnable r, ThreadPoolExecutor e) { // 可做日志记录等 System.err.println(r.toString() + " rejected" ); // System.out.println("completedTaskCount: " + e.getCompletedTaskCount()); } } // 线程工厂类 static class NameTreadFactory implements ThreadFactory { private final AtomicInteger mThreadNum = new AtomicInteger( 1 ); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement()); System.out.println(t.getName() + " has been created" ); return t; } } }
输出结果如下:
该线程池同一时间最多处理4个任务,2个任务处于等待对列中,其余的任务都会被拒绝执行。
Tips:
在默认情况下,只有当新任务到达时,才开始创建和启动核心线程,但是我们可以使用 prestartCoreThread()
和 prestartAllCoreThreads()
方法动态调整。
prestartCoreThread() :创一个空闲任务线程等待任务的到达
prestartAllCoreThreads() :创建核心线程池数量的空闲任务线程等待任务的到达
线程池的处理流程主要分为3步:
- 当在execute(Runnable)方法中提交的新任务后,线程池先判断线程数是否达到了核心线程数(corePoolSize)。如果未达到线程数,则创建核心线程处理任务;否则,就执行下一步;
- 接着线程池判断任务队列是否满了。如果没满,则将任务添加到任务队列中;否则,执行下一步;
- 接着因为任务队列满了,线程池就判断线程数是否达到了最大线程数。如果未达到,则创建非核心线程处理任务;否则,就执行饱和策略,默认会抛出RejectedExecutionException异常。
通过设置corePoolSize和maximumPoolSize相同,您可以创建一个固定大小的线程池。
通过将maximumPoolSize设置为基本上无界的值,例如Integer.MAX_VALUE,您可以允许池容纳任意数量的并发任务。
通常,核心和最大池大小仅在构建时设置,但也可以使用
setCorePoolSize
和setMaximumPoolSize
进行动态更改。
饱和策略:RejectedExecutionHandler
拒绝任务有两种情况:1. 线程池已经被关闭;2. 任务队列已满且maximumPoolSizes已满;
无论哪种情况,都会调用RejectedExecutionHandler的rejectedExecution方法。预定义了四种处理策略:
无论哪种情况,都会调用RejectedExecutionHandler的rejectedExecution方法。预定义了四种处理策略:
- AbortPolicy:默认策略,抛出RejectedExecutionException运行时异常;
- CallerRunsPolicy:这提供了一个简单的反馈控制机制,可以减慢提交新任务的速度;
- DiscardPolicy:直接丢弃新提交的任务;
- DiscardOldestPolicy:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程);
我们可以自己定义RejectedExecutionHandler,以适应特殊的容量和队列策略场景中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?