线程池运行原理分析

要想分析透彻整个线程池运行的逻辑,是个庞杂的工程,牵扯到线程池生命周期管理,队列管理,拒绝策略,调配逻辑等等.这里只是从一个Runnable任务发布到线程池中以后,线程池内部的运行逻辑角度去尝试分析.

先贴出整理的线程池操作流程图 , 然后开始追源码:

线程池运行流程图.png

线程数量控制策略

ThreadPoolExecutor是线程池的实现类,无论是自定义线程池,还是使用系统提供的线程池,都会使用到这个类.通过类的execute(Runnable command)方法来执行Runnable任务.
那么一旦将一个Runnable任务execute()以后,到底发生了什么? 直接看代码

/**
 * 将该Runnable任务加入线程池并在未来某个时刻执行
 * 该任务可能执行在一个新的线程 或 一个已存在的线程池中的线程
 * 如果该任务提交失败,可能是因为线程池已关闭,或者已达到线程池队列和线程数已满.
 * 该Runnable将交给RejectedExecutionHandler处理,抛出RejectedExecutionException
 */
public void execute(Runnable command) {
    if (command == null){
        //如果没传入Runnable任务,则抛出空指针异常
        throw new NullPointerException();
    }

    int c = ctl.get();
    //当前线程数 小于 核心线程数
    if (workerCountOf(c) < corePoolSize) {
        //直接开启新的线程,并将Runnable传入作为第一个要执行的任务,成功返回true,否则返回false
        if (addWorker(command, true)){
            return;
        }
        c = ctl.get();
    }

    //c < SHUTDOWN代表线程池处于RUNNING状态 + 将Runnable添加到任务队列,如果添加成功返回true失败返回false
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //成功加入队列后,再次检查是否需要添加新线程(因为已存在的线程可能在上次检查后销毁了,或者线程池在进入本方法后关闭了)
        if (! isRunning(recheck) && remove(command)){
            //如果线程池处于非RUNNING状态 并且 将该Runnable从任务队列中移除成功,则拒绝执行此任务
            //交给RejectedExecutionHandler调用rejectedExecution方法,拒绝执行此任务
            reject(command);
        }else if (workerCountOf(recheck) == 0){
            //如果线程池线程数量为0,则创建一条新线程,去执行
            addWorker(null, false);
        }   
    }else if (!addWorker(command, false))
        //如果线程池处于非RUNNING状态 或 将Runnable添加到队列失败(队列已满导致),则执行默认的拒绝策略
        reject(command);
}

整理流程如下:
1. 如果线程池中的线程数量少于corePoolSize(核心线程数量),那么会直接开启一个新的核心线程来执行任务,即使此时有空闲线程存在.
2. 如果线程池中线程数量大于等于corePoolSize(核心线程数量),那么任务会被插入到任务队列中排队,等待被执行.此时并不添加新的线程.
3. 如果在步骤2中由于任务队列已满导致无法将新任务进行排队,这个时候有两种情况:

  • 线程数量 [未] 达到maximumPoolSize(线程池最大线程数) , 立刻启动一个非核心线程来执行任务.
  • 线程数量 [已] 达到maximumPoolSize(线程池最大线程数) , 拒绝执行此任务.ThreadPoolExecutor会通过RejectedExecutionHandler,抛出RejectExecutionException异常.

以上就是一旦将一个Runnable任务execute()以后,执行的一系列逻辑,理解起来并不难,下面再对其中调用的一些方法做一些追查,就更方便理解其中的运行逻辑.

线程数量及线程池状态管理

我们发现在execute()方法中频繁的执行这句c = ctl.get();代码,那么这ctl是什么,get()方法获取到的是什么,获取到的c又用来做什么?

上源码:

//创建AtomicInteger对象
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //32-3 = 29
//最大线程容量
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; //将1的二进制向右位移29位,再减1

//运行状态保存在int值的高3位 (所有数值左移29位)
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

//运行状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
//线程数量
private static int workerCountOf(int c)  { return c & CAPACITY; }
//是否正在运行
private static boolean isRunning(int c) { return c < SHUTDOWN;}

以上代码中的信息整理如下:

  • clt是一个AtomicInteger对象,(提供原子操作进行Integer的使用,适用于高并发场景.该AtomicInteger的value可以自动刷新,确保在高并发环境下的唯一性.),而ctl.get()获取的就是该value值.
  • 线程池用一个AtomicInteger来保存 [线程数量] 和 [线程池状态] ,一个int数值一共有32位,高3位用于保存运行状态,低29位用于保存线程数量
  • 系统默认的线程容量就是(2^29)-1 , 大约5亿条线程-_-!

所以由此得知 :
频繁的调用c = ctl.get();是为了获取该AtomicInteger的最新值,进而通过位运算获取线程池的最新运行状态,线程数量.

[线程池状态]:

  • RUNNING: 接收新任务,并执行队列中的任务
  • SHUTDOWN: 不接收新任务,但是执行队列中的任务
  • STOP: 不接收新任务,不执行队列中的任务,中断正在执行中的任务
  • TIDYING: 所有的任务都已结束,线程数量为0,处于该状态的线程池即将调用terminated()方法
  • TERMINATED: terminated()方法执行完成

新线程的创建

在execute()方法中获知通过addWorker()方法来添加新线程,那么到底是如何添加和管理的?
开始追源码,一看究竟.

/**
 * 往线程池中添加Worker对象
 * @param  firstTask 线程中第一个要执行的任务 
 * @param  core      是否为核心线程
 * @return           添加是否成功
 */
 private boolean addWorker(Runnable firstTask, boolean core) {
    //这里有两层[死循环],外循环:不停的判断线程池的状态
    retry: for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            //一系列判断条件:线程池关闭,Runnable为空,队列为空,则直接return false,代表Runnable添加失败
            if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())){
                return false;
            }

            //内循环:不停的检查线程容量        
            for (;;) {
                int wc = workerCountOf(c);
                //超过线程数限制,则return false
                if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)){
                    return false;
                }
                //★ 添加线程成功,则直接跳出两层循环,继续往下执行.
                //注意:这里只是把线程数成功添加到了AtomicInteger记录的线程池数量中,真正的Runnable添加,在下面的代码中进行
                if (compareAndIncrementWorkerCount(c)){
                    break retry;
                }
                //再次判断线程池最新状态,如果状态改变了(内循环和外循环记录的状态不符),则重新开始外层死循环
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs){
                    continue retry;
                }
            }
        }

    //结束循环之后,开始真正的创建线程.
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //创建一个Worker对象,并将Runnable当做参数传入
        w = new Worker(firstTask);
        //从worker对象中取出线程
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            //拿到锁
            mainLock.lock();
            try {
                //再次检查线程池最新状态
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    //检查准备执行Runnable的Thread的状态,如果该Thread已处于启动状态,则抛出状态异常(因为目前还没启动呢)
                    if (t.isAlive()){
                        throw new IllegalThreadStateException();
                    } 
                    //将新创建的worker,添加到worker集合
                    workers.add(w);
                    ...
                    workerAdded = true;
                }
            } finally {
                //释放锁
                mainLock.unlock();
            }
            if (workerAdded) {
                //★Thread开始启动
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        //添加worker失败
        if (! workerStarted){
            addWorkerFailed(w);
        }
    }
    return workerStarted;
}

总结:
1. 先判断线程池状态和线程池中线程的容量,如果满足线程添加的条件,则先把AtomicInteger中记录的线程数量+1.然后再进行线程添加的工作.
2. 创建worker对象,并将Runnable作为参数传递进去,并从worker中取出Thread对象,进行一系列条件判断后.开启Thread的start()方法,线程开始运行.所以worker对象中必然包含了一个Thread和一个要被执行的Runnable.

那么接下来继续追源码,印证下第二点的推断,看看Worker到底干了什么.

Worker类

//ThreadPoolExecutor的内部finial类
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{

    //当前worker要执行任务所用的线程(如果创建失败,则可能是null)
    final Thread thread;
    //第一个要执行的任务(可能是null)
    Runnable firstTask;
    //当前线程执行完的任务总数
    volatile long completedTasks;

    //通过构造传入Runnable任务
    Worker(Runnable firstTask) {
        ...
        this.firstTask = firstTask;
        //通过ThreadFactory()创建新线程
        this.thread = getThreadFactory().newThread(this);
    }

    //调用外部类runWorker()方法
    public void run() {
        runWorker(this);
    }
    ...
}

worker类中的内部实现也印证了我们的推断:

  • 每个worker,都是一条线程,同时里面包含了一个firstTask,即初始化时要被首先执行的任务.
  • 最终执行任务的,是runWorker()方法

线程的复用

继续追runWorker()方法的源码

//ThreadPoolExecutor的final类,该方法由内部类Worker的run()方法调用
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    //取出Worker对象中的Runnable任务
    Runnable task = w.firstTask;
    boolean completedAbruptly = true;
    ...
    try {
        //★注意这个while循环,在这里实现了 [线程复用]
        while (task != null || (task = getTask()) != null) {
            //上锁
            w.lock();
            //检查Thread状态的代码
            ...
            try {
                ...
                try {
                    //执行Worker中的Runnable任务
                    task.run();
                } catch (...) {
                   ...catch各种异常
                } 
            } finally {
                //置空任务(这样下次循环开始时,task依然为null,需要再通过getTask()取) + 记录该Worker完成任务数量 + 解锁
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        //该线程已经从队列中取不到任务了,改变标记
         completedAbruptly = false;
    } finally {
        //线程移除
        processWorkerExit(w, completedAbruptly);
    }
}

通过上面的源码,发现通过一个while循环,不断的getTask()取任务出来执行,以这种方式实现了线程的复用.

线程复用逻辑整理如下:
1. 如果task不为空,则开始执行task
2. 如果task为空,则通过getTask()再去取任务,并赋值给task,如果取到的Runnable不为空,则执行该任务
3. 执行完毕后,通过while循环继续getTask()取任务
4. 如果getTask()取到的任务依然是空,那么整个runWorker()方法执行完毕

上面只是从getTask()方法名和其返回值来猜测此方法的作用,下面就继续追源码,来证实和研究getTask()到底是怎么取任务的,从哪取,怎么取.

getTask()

private Runnable getTask() {
    ...
    for (;;) {
        ...
        // 如果线程池已关闭 或 任务队列为空,则AtomicInteger中记录的线程数量-1,并return null,结束本方法
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        //获取当前线程池中的总线程数
        int wc = workerCountOf(c);
        //allowCoreThreadTimeOut参数是使用者自行设置的(默认false),用来设置:是否允许核心线程有超时策略
        //条件1:核心线程超时 条件2:当前线程数 > 核心线程数,满足任何一个条件则timed标记为true 
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        //超过最大线程数 或 超时 或 任务队列为空...  线程数量-1 + return null
        ...
        try {
            //根据timed标记,使用不同的方式(限时等待 or 阻塞)从BlockingQueue<Runnable> workQueue 队列中取任务
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
            if (r != null){
                //如果取到了,就将Runnable返回
                return r;
            }
            //如果没取到,则重新for循环
            ...
        }
    }
}

将以上源码中的信息整理如下:

  • 线程池使用BlockingQueue来管理整个线程池中的Runnable任务,变量workQueue存放的都是待执行的任务
  • BlockingQueue是个阻塞队列,BlockingQueue.take()方法如果得到的是空,则进入等待状态,直到BlockingQueue有新的对象被加入时,才可以正常将Runnable取出并返回,线程开始正常运转,正常执行Runnable任务。
/**
 * 先进先出的阻塞队列
 */
public interface BlockingQueue<E> extends Queue<E> {
    /**
     * 检索并移除队列的顶部元素,如果该元素不可用则等待,直至元素可用
     * Retrieves and removes the head of this queue, waiting if necessary
     * until an element becomes available.
     *
     * @return the head of this queue
     * @throws InterruptedException if interrupted while waiting
     */
    E take()
    ...
}

让我们整理一下上面几段源码的逻辑顺序:
1. execute()方法执行之后,进行一系列的逻辑判断来控制线程池中的线程数量,并通过addWorker()方法创建新线程
2. 一旦Worker里的Thread开始start()之后,执行的其实是Worker里的run()方法,run()方法调用runWorker(Worker w)方法.
3. 在runWorker()方法里面通过getTask()方法不停的取workQueue队列中的任务来执行,如果取到了就执行,如果没取到就等待.

结论:

  • 一旦一个线程开启之后,会一直执行下去,直至任务队列中的任务执行完毕,达成了线程的复用
  • 以Runnable队列为目标的worker虽然是串行操作,但是由于可以通过addWorker()添加多个worker,并且多个worker取的是同一个BlockingQueue中的Runnable,所以就实现了并行处理.

线程的移除

在runWorker()方法中有如下代码:

final void runWorker(Worker w) {
    boolean completedAbruptly = true;
    ...
    try {
        while (getTask()...) {
            ...
            处理任务
        }
        //该线程已经从队列中取不到任务了,改变标记,该标记表示:该线程是否因用户因素导致的异常而终止
         completedAbruptly = false;
    } finally {
        //线程移除
        processWorkerExit(w, completedAbruptly);
    }
}

processWorkerExit这里用来将worker从worker集合中移除,步骤如下:
1. 先移除传入的Worker(线程)
2. 判断线程池里的最少线程数,如果最少线程数为0条,但是队列里依然有任务未执行完毕.那么必须确保线程池中至少有1条线程.(将最小线程数置为1)
3. 如果当前线程数 > 最小线程数,本方法结束,不再往下执行
4. 否则添加一条新线程,来替代当前线程,继续去执行队列中的任务.

/**
 * @param w the worker 线程
 * @param completedAbruptly 该线程是否因用户因素导致的异常而终止
 */
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    ...
    try {
        //记录该线程完成任务的总数
        completedTaskCount += w.completedTasks;
        //从worker集合中移除本worker(线程)
        workers.remove(w);
    }
    ...
    //如果在runWoker()中正常执行任务完毕,这里completedAbruptly传入的就是false
    if (!completedAbruptly) {
        int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
        //如果线程池里最少线程数为0,但是此时任务队列里依然还有任务
        if (min == 0 && ! workQueue.isEmpty()){
            //那么必须保留一条线程,所以将最小值设置为1
            min = 1;
        }
        //如果当前线程数>= 最小线程数,则直接return
        if (workerCountOf(c) >= min){
            return; 
        }
    }
    //否则添加一条新线程,来替代当前线程,继续去执行队列中的任务.
    addWorker(null, false);
}

这次源码分析就先到这里,一路从execute()开始,走到线程移除.其实线程池里面涉及到的问题很多,以后有时间再慢慢研究.

参考链接:
从使用到原理学习Java线程池

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
《Android系统源代码情景分析》随书光盘内容(源代码) 目录如下: 第1篇 初识Android系统 第1章 准备知识 1.1 Linux内核参考书籍 1.2 Android应用程序参考书籍 1.3 下载、编译和运行Android源代码 1.3.1 下载Android源代码 1.3.2 编译Android源代码 1.3.3 运行Android模拟器 1.4 下载、编译和运行Android内核源代码 1.4.1 下载Android内核源代码 1.4.2 编译Android内核源代码 1.4.3 运行Android模拟器 1.5 开发第一个Android应用程序 1.6 单独编译和打包Android应用程序模块 1.6.1 导入单独编译模块的mmm命令 1.6.2 单独编译Android应用程序模块 1.6.3 重新打包Android系统镜像文件 第2章 硬件抽象层 2.1 开发Android硬件驱动程序 2.1.1 实现内核驱动程序模块 2.1.2 修改内核Kconfig文件 2.1.3 修改内核Makefile文件 2.1.4 编译内核驱动程序模块 2.1.5 验证内核驱动程序模块 2.2 开发C可执行程序验证Android硬件驱动程序 2.3 开发Android硬件抽象层模块 2.3.1 硬件抽象层模块编写规范 2.3.2 编写硬件抽象层模块接口 2.3.3 硬件抽象层模块的加载过程 2.3.4 处理硬件设备访问权限问题 2.4 开发Android硬件访问服务 2.4.1 定义硬件访问服务接口 2.4.2 实现硬件访问服务 2.4.3 实现硬件访问服务的JNI方法 2.4.4 启动硬件访问服务 2.5 开发Android应用程序来使用硬件访问服务 第3章 智能指针 3.1 轻量级指针 3.1.1 实现原理分析 3.1.2 应用实例分析 3.2 强指针和弱指针 3.2.1 强指针的实现原理分析 3.2.2 弱指针的实现原理分析 3.2.3 应用实例分析 第2篇 Android专用驱动系统 第4章 Logger日志系统 4.1 Logger日志格式 4.2 Logger日志驱动程序 4.2.1 基础数据结构 4.2.2 日志设备的初始化过程 4.2.3 日志设备文件的打开过程 4.2.4 日志记录的读取过程 4.2.5 日志记录的写入过程 4.3 运行时库层日志库 4.4 C/C++日志写入接口 4.5 Java日志写入接口 4.6 Logcat工具分析 4.6.1 相关数据结构 4.6.2 初始化过程 4.6.3 日志记录的读取过程 4.6.4 日志记录的输出过程 第5章 Binder进程间通信系统 5.1 Binder驱动程序 5.1.1 基础数据结构 5.1.2 Binder设备的初始化过程 5.1.3 Binder设备文件的打开过程 5.1.4 Binder设备文件的内存映射过程 5.1.5 内核缓冲区管理 5.2 Binder进程间通信库 5.3 Binder进程间通信应用实例 5.4 Binder对象引用计数技术 5.4.1 Binder本地对象的生命周期 5.4.2 Binder实体对象的生命周期 5.4.3 Binder引用对象的生命周期 5.4.4 Binder代理对象的生命周期 5.5 Binder对象死亡通知机制 5.5.1 注册死亡接收通知 5.5.2 发送死亡接收通知 5.5.3 注销死亡接收通知 5.6 Service Manager的启动过程 5.6.1 打开和映射Binder设备文件 5.6.2 注册为Binder上下文管理者 5.6.3 循环等待Client进程请求 5.7 Service Manager代理对象的获取过程 5.8 Service组件的启动过程 5.8.1 注册Service组件 5.8.2 启动Binder线程池 5.9 Service代理对象的获取过程 5.10 Binder进程间通信机制的Java接口 5.10.1 Service Manager的Java代理对象的获取过程 5.10.2 Java服务接口的定义和解析 5.10.3 Java服务的启动过程 5.10.4 Java服务代理对象的获取过程 5.10.5 Java服务的调用过程 第6章 Ashmem匿名共享内存系统 6.1 Ashmem驱动程序 6.1.1 基础数据结构 6.1.2 匿名共享内存设备的初始化过程 6.1.3 匿名共享内存设备文件的打开过程 6.1.4 匿名共享内存设备文件的内存映射过程 6.1.5 匿名共享内存块的锁定和解锁过程 6.1.6 匿名共享内存块的回收过程 6.2 运行时库cutils的匿名共享内存访问接口 6.3 匿名共享内存的C++访问接口 6.3.1 MemoryHeapBase 6.3.2 MemoryBase 6.3.3 应用实例 6.4 匿名共享内存的Java访问接口 6.4.1 MemoryFile 6.4.2 应用实例 6.5 匿名共享内存的共享原理 第3篇 Android应用程序框架 第7章 Activity组件的启动过程 7.1 Activity组件应用实例 7.2 根Activity组件的启动过程 7.3 子Activity组件在进程内的启动过程 7.4 子Activity组件在新进程中的启动过程 第8章 Service组件的启动过程 8.1 Service组件应用实例 8.2 Service组件在新进程中的启动过程 8.3 Service组件在进程内的绑定过程 第9章 Android系统广播机制 9.1 广播机制应用实例 9.2 广播接收者的注册过程 9.3 广播的发送过程 第10章 Content Provider组件的实现原理 10.1 Content Provider组件应用实例 10.1.1 ArticlesProvider 10.1.2 Article 10.2 Content Provider组件的启动过程 10.3 Content Provider组件的数据共享原理 10.3.1 数据共享模型 10.3.2 数据传输过程 10.4 Content Provider组件的数据更新通知机制 10.4.1 注册内容观察者 10.4.2 发送数据更新通知 第11章 Zygote和System进程的启动过程 11.1 Zygote进程的启动脚本 11.2 Zygote进程的启动过程 11.3 System进程的启动过程 第12章 Android应用程序进程的启动过程 12.1 应用程序进程的创建过程 12.2 Binder线程池的启动过程 12.3 消息循环的创建过程 第13章 Android应用程序的消息处理机制 13.1 创建线程消息队列 13.2 线程消息循环过程 13.3 线程消息发送过程 13.4 线程消息处理过程 第14章 Android应用程序的键盘消息处理机制 14.1 键盘消息处理模型 14.2 InputManager的启动过程 14.2.1 创建InputManager 14.2.2 启动InputManager 14.2.3 启动InputDispatcher 14.2.4 启动InputReader 14.3 InputChannel的注册过程 14.3.1 创建InputChannel 14.3.2 注册Server端InputChannel 14.3.3 注册系统当前激活的应用程序窗口 14.3.4 注册Client端InputChannel 14.4 键盘消息的分发过程 14.4.1 InputReader获得键盘事件 14.4.2 InputDispatcher分发键盘事件 14.4.3 系统当前激活的应用程序窗口获得键盘消息 14.4.4 InputDispatcher获得键盘事件处理完成通知 14.5 InputChannel的注销过程 14.5.1 销毁应用程序窗口 14.5.2 注销Client端InputChannel 14.5.3 注销Server端InputChannel 第15章 Android应用程序线程的消息循环模型 15.1 应用程序主线程消息循环模型 15.2 与界面无关的应用程序子线程消息循环模型 15.3 与界面相关的应用程序子线程消息循环模型 第16章 Android应用程序的安装和显示过程 16.1 应用程序的安装过程 16.2 应用程序的显示过程
Spring中的线程池是通过ThreadPoolTaskExecutor类来实现的,该类最终调用了Java中的ThreadPoolExecutor类的一些方法来实现线程池的功能。线程池的工作原理如下: 1. 配置线程池大小:可以通过ThreadPoolTaskExecutor类的setCorePoolSize()方法和setMaxPoolSize()方法来配置线程池的核心线程数和最大线程数。核心线程数是线程池中保持活动状态的线程数,最大线程数是线程池中允许的最大线程数。 2. 提交任务:可以使用execute()方法向线程池提交任务。execute()方法是Executor接口中声明的方法,在ThreadPoolExecutor类中有具体的实现。该方法将任务提交给线程池,由线程池中的线程执行。 3. 执行任务:线程池会根据配置的核心线程数和最大线程数来管理线程的数量。当有任务提交时,线程池会根据当前线程数和任务队列的情况来决定是创建新的线程还是将任务放入队列中等待执行。如果线程池中的线程数小于核心线程数,则会创建新的线程来执行任务。如果线程池中的线程数已经达到核心线程数,但任务队列未满,则会将任务放入队列中等待执行。如果线程池中的线程数已经达到核心线程数且任务队列已满,但线程池中的线程数小于最大线程数,则会创建新的线程来执行任务。如果线程池中的线程数已经达到最大线程数且任务队列已满,则会根据配置的拒绝策略来处理无法执行的任务。 4. 返回结果:execute()方法没有返回值,而submit()方法有返回值。可以使用submit()方法向线程池提交任务,并获取任务的执行结果。 总结起来,Spring的线程池工作原理是通过ThreadPoolTaskExecutor类来实现的,该类调用了Java中的ThreadPoolExecutor类的一些方法来管理线程池的大小和执行任务。通过配置线程池的核心线程数、最大线程数和任务队列,线程池可以根据任务的提交情况来动态地管理线程的数量,并通过拒绝策略来处理无法执行的任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crocutax

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值