剖析ThreadPoolExecutor

本文深入剖析了Java中的ThreadPoolExecutor线程池,包括其基本使用、体系结构、核心机制和常见API。线程池通过缓存线程和管理资源提高性能,提供了不同构造器以满足不同需求。核心机制包括AtomicInteger ctl用于存储线程状态和线程数量,以及 BlockingQueue workQueue 用于保存未执行任务。此外,文章还讨论了线程池的启动、停止和拒绝策略,以及如何通过源码理解其工作原理。
摘要由CSDN通过智能技术生成

简单介绍

ThreadPoolExecutor 就是大家常说的线程池, 顾名思义它就是一个池子, 里面有一批称为线程的东西 ,使用者向线程池提交任务, 不用关心是那个线程执行了任务(不关心其底层实现), 只要关心任务是否正常结束即可, 甚至都可以不关心任务是否正常结束, 只管提交. 并且使用者提交完任务之后, 其可以解放出来去做其他的工作, 如果使用者关心任务的状态或者结果, 则可以选择对应的提交任务方式.

基本使用

基于上述线程池的简单介绍, 可以得出ThreadPoolExecutor主要提供两种能力

  1. 线程缓存: 执行大量异步任务时通过缓存一定数量的线程减少线程切换,提供更好的性能.
  2. 资源管理: 当执行任务时, 提供有限的资源供执行任务使用, 并且提供基本的统计结果.

使用ThreadPoolExecutor, 需要了解它的几个构造器. 它的构造器如下图. 手动构建线程池最小需要写5个参数, 最多要写7个.
在这里插入图片描述
上图中, 从上到下共4个构造器, 前三个构造器都最终调用了第四个构造器. 第四个构造器共有7个参数, 从左到右, 在下面表格中分别介绍7个参数.

参数 类型 参数含义
corePoolSize int 当提交任务时, 如果发现当前运行的线程数小于corePoolSize, 则会新创建线程去处理该任务, 并且当其他新建的线程闲置时也会创建线程
maximumPoolSize int 当线程数量大于corePoolSize的配置, 同时队列任务放满了, 则会创建新的线程处理,但是创建之后的线程总数量并不能大于maximumPoolSize配置的
keepAliveTime long 当大于corePoolSize的线程闲置时间大于配置时间, 则会被终止
unit TimeUnit keepAlieTime的时间单位, TimeUnit.SECONDS, TimeUnit.HOURS
workQueue BlockingQueue 没有运行的任务将会保存在该队列中, 当开始运行则会移除
threadFactory ThreadFactory 提供线程, 该参数有默认值
handler RejectExecutionHandler 拒绝策略, 这里的拒绝并不是不执行, 该参数有默认值(AbortPolicy, 抛出异常)

只看上面的文字有点难以理解, 下面通过新建一个简单的线程池来了解一下.

// corePoolSize = 5
// maximumPoolSize = 10
// keepAliveTime = 3L
// unit = TimeUnit.SECOND
// workQueue = LinkedBlockingQueue(10)
// threadFactory 采用默认值
// handler 采用默认值
ExecutorService executorService = 
	new ThreadPoolExecutor(5, 10, 3L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));

上面创建的线程池最多能同时存在20个任务, 其中10个是运行的, 而剩余的10个存储在阻塞链表中. 当两者全部满了(有20个任务), 再提交任务会被拒绝(前面提到的默认handler,抛出异常), 需要注意10个运行的线程并不是提交10个任务立马就会启动10个线程, 而是先启动5个, 因为corePoolSize等于5, 其余的任务放到阻塞链表中, 若阻塞链表中存放的任务溢出, 才会启动新的线程, 启动的线程总数受maximumPoolSize限制, 并且启动的线程数量超过corePoolSize的限制之后, 超过的线程受配置最长闲置时间的影响, 若超时还未有任务产生, 则线程会退出.

基于上面创建的线程池,来看下常用的API.

最基本的提交任务API是execute, 它接受一个对象, 对象必须实现Runnable接口. 提交任务之后, 任务则会在未来某个时间执行. 这里需要注意, 该方法是没有返回值的. void execute(Runnable) 方法是Executor 接口中定义的, 它也是Executor唯一的方法.

void executorService.executor(Runnable);

下面是使用submit进行进行, 它是一类方法, 提供更强大的适配性.

// submit提交方式(一)
// 用Future封装了call方法的返回值.
Future<String> result = executorService.submit(new Callable<String> {
   
    @Override
    public String call() throws Exception {
   
        return "hello, world";
    }
});

// submit提交方式(二)
// 如果任务结束, 则Future中封装的是null.
Future<?> result = executorService.submit(new Runnable() {
   
    @Override
    public void run() {
   
        return;
    }
});

// submit提交方式(三)
// 当任务结束之后, 返回传入的结果, 这里是"SUCCESS"
// 任务结束之后: result.get() 返回 "SUCCESS"
Future<String> result = executorService.submit(new Runnable() {
   
    @Override
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值