Java多线程进阶(39)—— J.U.C之executors框架:ThreadPoolExecutor

本文详细介绍了Java中ThreadPoolExecutor线程池。先介绍其继承体系,包括AbstractExecutorService;接着阐述基本原理,如构造线程池、线程池状态和线程管理;然后说明调度流程,涵盖工作线程的创建、执行、清理,任务获取及拒绝策略;还提及线程池关闭方法。最后强调要合理设置参数和选择阻塞队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

一、ThreadPoolExecutor简介

juc-executors框架概述的章节中,我们已经简要介绍过ThreadPoolExecutor了,通过Executors工厂,用户可以创建自己需要的执行器对象。ThreadPoolExecutor,它是J.U.C在JDK1.5时提供的一种实现了ExecutorService接口的执行器,或者说线程池。

ThreadPoolExecutor并没有自己直接实现ExecutorService接口,因为它只是其中一种Executor的实现而已,所以Doug Lea把一些通用部分封装成一个抽象父类—— AbstractExecutorService ,供J.U.C中的其它执行器继承。如果读者需要自己实现一个Executor,也可以继承该抽象类。

1.1 AbstractExecutorService

AbstractExecutorService 提供了 ExecutorService 接口的默认实现——主要实现了 submit、invokeAny 、invokeAll这三类方法,如果读者看过上一篇综述文章,就应该知道,ExecutorService的这三类方法几乎都是返回一个Future对象。而Future是一个接口,AbstractExecutorService既然实现了这些方法,必然要实现该Future接口,我们来看下AbstractExecutorService实现的 submit 方法:

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

可以看到,上述方法首先对Runnable和返回值value进行了封装,通过newTaskFor方法,封装成了一个 FutureTask 对象,然后通过execute方法执行任务,最后返回异步任务对象。

这里其实是模板方法模式的运用,execute是抽象方法,需要由继承AbstractExecutorService的子类来实现。

上述需要注意的是newTaskFor方法,该方法创建了一个Future对象:

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

FutureTask其实就是Future接口的实现类:

我们之前讲过,J.U.C中的Future接口是“Future模式”的多线程设计模式的实现,可以让调用方以异步方式获取任务的执行结果。而FutureTask便是这样一类支持异步返回结果的任务,既然是任务就需要实现Runnable接口,同时又要支持异步功能,所以又需要实现Future接口。J.U.C为了方便,新定义了一个接口—— RunnableFuture ,该接口同时继承Runnable和Future,代表支持异步处理的任务,而FutureTask便是它的默认实现。

本节不会在Futrure模式上花费太多笔墨,以后我们会专门讲解J.U.C对Future模式的支持。

1.2 线程池简介

回到ThreadPoolExecutor,从该类的命名也可以看出,这是一种线程池执行器。线程池大家应该并不陌生,应用开发中经常需要用到数据库连接池,数据库连接池里维护着一些数据库连接,当应用需要连接数据库时,并不是自己创建连接,而是从连接池中获取可用连接;当关闭数据库连接时,只是将该连接还给连接池,以供复用。

而线程池也是类似的概念,当有任务需要执行时,线程池会给该任务分配线程,如果当前没有可用线程,一般会将任务放进一个队列中,当有线程可用时,再从队列中取出任务并执行,如下图:

线程池的引入,主要解决以下问题:

  1. 减少系统因为频繁创建和销毁线程所带来的开销;
  2. 自动管理线程,对使用方透明,使其可以专注于任务的构建。

二、ThreadPoolExecutor基本原理

了解了线程池和ThreadPoolExecutor的继承体系,接下来,我们来看下J.U.C是如何实现一个普通线程池的。

2.1 构造线程池

我们先来看下ThreadPoolExecutor的构造器,其实之前在讲Executors时已经接触过了,Executors工厂方法创建的三种线程池:newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool,内部都是通过ThreadPoolExecutor的下面这个构造器实例化了ThreadPoolExecutor对象:

    /**
     * 使用给定的参数创建ThreadPoolExecutor.
     *
     * @param corePoolSize    核心线程池中的最大线程数
     * @param maximumPoolSize 总线程池中的最大线程数
     * @param keepAliveTime   空闲线程的存活时间
     * @param unit            keepAliveTime的单位
     * @param workQueue       任务队列, 保存已经提交但尚未被执行的线程
     * @param threadFactory   线程工厂(用于指定如果创建一个线程)
     * @param handler         拒绝策略 (当任务太多导致工作队列满时的处理策略)
     */
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                              BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值