线程池之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,您可以允许池容纳任意数量的并发任务。
通常,核心和最大池大小仅在构建时设置,但也可以使用setCorePoolSizesetMaximumPoolSize进行动态更改。
饱和策略:RejectedExecutionHandler
拒绝任务有两种情况:1. 线程池已经被关闭;2. 任务队列已满且maximumPoolSizes已满;
无论哪种情况,都会调用RejectedExecutionHandler的rejectedExecution方法。预定义了四种处理策略:
  • AbortPolicy:默认策略,抛出RejectedExecutionException运行时异常;
  • CallerRunsPolicy:这提供了一个简单的反馈控制机制,可以减慢提交新任务的速度;
  • DiscardPolicy:直接丢弃新提交的任务;
  • DiscardOldestPolicy:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程);
    我们可以自己定义RejectedExecutionHandler,以适应特殊的容量和队列策略场景中。
posted @   danielzzz  阅读(41)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示