掘金 后端 ( ) • 2024-06-26 21:20

在面试时,有的面试官就喜欢这种刁钻角度的面试问题,如果你对线程池的任务执行流程熟悉的话,那么该题就不会难住你。所以在开始之前,我们先看下线程池相关的知识。

图片

本文使用 JDK8 演示。

一、概念

线程池是Java中管理和重用线程的一种方式,也是实现并发编程的一种手段。通过使用线程池可以显著提升多线程应用程序的性能。

线程池相对于线程来说,线程池主要解决了两个问题,一个是线程的创建和销毁代价大,另一个就是多线程并发执行时可能导致系统资源不足的问题。

通过线程池,可以提前创建好一组一定数量的线程,并管理好这些线程的生命周期,也就是线程池中线程的存活时间,通过这些我们就可以在有需要的时候重用这些线程,减少创建和销毁线程的开销,提升系统的响应速度和系统资源利用率。

二、线程池工作流程

线程池的任务执行流程是怎么样的呢?

1、首先会判断当前工作线程数量是否大于核心线程**数量(corePoolSize),如果小于核心线程数量,直接创建线程执行任务。如果大于核心线程数量,就将任务放入任务队列中进行缓存。

2、判断任务队列容量是否已满,如果不满,任务放入任务队列。

3、如果任务队列满了,判断当前工作线程数量是否大于最大线程数量(maximumPoolSize),如果小于最大线程数量,创建线程执行任务。

4、当工作线程已经大于最大线程了,此时,任务会触发拒绝策略,默认的拒绝策略是抛出异常。

任务工作流程如下图所示。

图片

到了这你心中有结果了吗,是不是线程池会把该任务丢入任务队列呢,不着急,慢慢来,下面我们一起看下源码中是如何判断的。

三、源码分析

写个测试方法

在实际的工作中要注意设置线程名称以及拒绝策略哦!

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,10,1000,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1000), new NamedThreadFactory("zuiyu",false));

        threadPoolExecutor.execute(()->{
            System.out.println("醉鱼Java");
        });
    }

Debug 一下

首先我们看一下execute方法就知道是如何了。

图片

通过 debug 发现,当代码运行到图中圈起来的代码这一行时,也就是当工作线程数量为0时,会执行下一步的创建线程执行任务。

图片

最后程序输出了《醉鱼Java》,所以当核心线程设置为 0 的时候,线程池会判断当前工作线程为0 时,会创建线程执行任务。

结论

通过简单的 debug 了一下任务的执行流程,核心线程设置为 0 时,当把任务丢入线程池,还是会把任务丢入任务队列,但是也会在下一步进行判断当前工作线程的线程数量是否为 0,如果为 0,也会创建线程进行执行任务。所以现在你懂了吗?如果你还不懂,欢迎评论区来交流一下,我们一起探讨。

来留一个课后题吧,就是核心线程设置为0,那么当最大线程设置为0 还会是这个执行流程吗?有知道的小伙伴可以评论区交流一下。(建议看一下源码哈,很简单,就在注释中,提示一下这其实是个线程池创建规则的问题)。