线程池面试必考

作者: 叫练

你对Java线程池了解吗?你有用过线程池吗?那先说下线程池核心参数吧。。。对不起,回去再看看吧。

image.png

为了一丝体面,们今天来整理几个面试中常考线程池面试问题吧!

为什么要用线程池?


  1. 线程复用。线程的重复使用是线程池设计的重点,如果需要开启1000个线程执行程序,系统会创建1000个线程,如果用线程池来执行1000个任务,并不需要开启1000个线程,只需要设置corePoolSize核心线程大小数量,最大线程数量,队列大小即可重复利用线程置换任务,而且1000个线程切换效率并不低,也就是说线程越多效率不一定高。所以在多线程环境实际开发中们推荐用多线程。
  2. 更好的管理线程。ThreadPoolExecutor可以控制线程数量,根据实际应用场景设置队列数量和饱和策略。

你说下线程池核心参数?


  • corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。
  • maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize
  • keepAliveTime :非核心线程的心跳时间。如果非核心线程在keepAliveTime内没有运行任务,非核心线程会消亡。
  • workQueue :阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue等,用来存放线程任务。
  • defaultHandler :饱和策略。
  • ThreadFactory :线程工厂。新建线程工厂。

execute任务添加流程?


image.png

  1. 线程池执行execute/submit方法向线程池添加任务,当任务小于核心线程数corePoolSize,线程池中可以创建新的线程。
  2. 当任务大于核心线程数corePoolSize,就向阻塞队列添加任务。
  3. 如果阻塞队列已满,需要通过比较参数maximumPoolSize,在线程池创建新的线程,当线程数量大于maximumPoolSize,说明当前设置线程池中线程已经处理不了了,就会执行饱和策略。

饱和策略知道吗?


上图们说过,当线程数量大于maximumPoolSize,就会执行饱和策略。ThreadPoolExecutor类中一共有4种饱和策略。通过实现RejectedExecutionHandler接口。

  • AbortPolicy : 线程任务丢弃报错。默认饱和策略。
  • DiscardPolicy : 线程任务直接丢弃不报错。
  • DiscardOldestPolicy : 将workQueue队首任务丢弃,将最新线程任务重新加入队列执行。
  • CallerRunsPolicy :线程池之外的线程直接调用run方法执行。

下面们在代码中看下饱和策略使用方式。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @author :jiaolian
 * @date :Created in 2021-02-20 16:28
 * @description:线程池丢弃策略
 * @modified By:
 * 公众号:叫练
 */
public class AbortTest {
    //线程数量
    private static final int THREAD_COUNT = 50;
    private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(THREAD_COUNT);
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(1);
    //线程池丢弃策略
    public static void main(String[] args) throws InterruptedException {
        //新建一个线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3,5,1, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(20),new ThreadPoolExecutor.AbortPolicy());
        //DiscardPolicy 丢弃
        //AbortPolicy 丢弃报错
        //DiscardOldestPolicy 将队列对首的任务丢弃,执行当前线程任务
        //CallerRunsPolicy 直接调用run方法
        //提交线程
        for (int i=0; i<THREAD_COUNT; i++) {
            threadPoolExecutor.execute(()->{
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" execute!"+ATOMIC_INTEGER.getAndIncrement());
                COUNT_DOWN_LATCH.countDown();
            });
        }
        COUNT_DOWN_LATCH.await();
        //关闭线程
        threadPoolExecutor.shutdown();
    }
}

如上代码:核心线程数量是3,最大线程数量是5,阻塞队列是20,共提交50个线程,这里饱和策略用的是AbortPolicy,分析执行线程池过程,线程池中首先开启3个核心线程Worker,发现3个线程处理不了50个线程任务,于是线程池就向阻塞队列添加任务,发现还是阻塞队列也容纳不下50个任务,于是又增加至2个线程同时运行线程任务,一共是5个线程同时运行任务,此时线程池中共有25个任务会被执行,还有25个任务会被丢弃,因为们用的是AbortPolicy饱和策略,会报错,截部分图如下划红线所示。一共执行了25个任务。其他几种策略大家可以参照执行。

image.png

你平时线程池怎么用的?


  • Excutors.newSingleThreadExecutor :1个corePoolSize,LinkedBlockingQueue队列无限大,当创建无数个线程,队列无限长,可能出现OOM内存溢出。单一线程。
  • Executors.newCachedThreadPool :0个corePoolSize,Interger.MAX_VALUE 最大线程数, 当创建无数个线程,可能出现OOM内存溢出。适用小而多线程。
  • Executors.newFixedThreadPool :n个corePoolSize,n个最大线程个数, LinkedBlockingQueue阻塞队列,当创建无数个线程,队列无限长,可能出现OOM内存溢出。适用固定线程。
  • Executors.newScheduledThreadPool :n个corePoolSize,Interger.MAX_VALUE个最大线程数, 当创建无数个线程,可能出现OOM内存溢出。

源码中线程池是怎么复用线程的?


源码中ThreadPoolExecutor中有个内置对象Worker,每个worker都是一个线程,worker线程数量和参数有关,每个worker会while死循环从阻塞队列中取数据,通过置换worker中Runnable对象,运行其run方法起到线程置换的效果,这样做的好处是避免多线程频繁线程切换,提高程序运行性能。

总结


今天们介绍了线程池中面试中几个重要的面试点,整理出来希望能对你有帮助,写的比不全,同时还有许多需要修正的地方,希望亲们加以指正和点评,喜欢的请点赞加关注哦。点关注,不迷路,叫练【公众号】,微信号【jiaolian123abc】边叫边练。

[] ) [](top

原文创作:叫练

原文链接:https://www.cnblogs.com/jiaolian/p/14434593.html

文章列表

更多推荐

更多
  • 九、Azure 发布管道——服务连接、模板、工件、阶段和环境 第 9.01 课:服务连接,第 9.02 课:使用模板,第 9.03 课:发布的工件,第 9.04 课:发布阶段,第 9.05 课:环境,摘要, 在前几章中,我们讨论了设置构建管道的特性和选项。构建管道允许您构建源代码并使用构建的二进制
    Apache CN

  • Azure ppl流水线-八、创建和使用 YAML 生成管道 第 8.01 课:YAML 管道入门,第 8.02 课:设置管道触发器和过滤器,第 8.03 课:在 YAML 中使用变量,第 8.04 课:管道中的作业和阶段,第 8.05 课:工作中的步骤和任务,第 8.06 课:使用模板,摘要,
    Apache CN

  • 十、Azure 发布管道——作业、部署组、变量和其他选项 第 10.01 课:代理作业,第 10.02 课:部署组作业,第 10.03 课:无代理作业,第 10.04 课:变量,第 10.05 课:其他有用的功能,摘要, 在前一章中,我们讨论了一些与发布管道相关的重要特性。描述了允许发布管理的
    Apache CN

  • Azure ppl流水线-十一、REST API、命令行和扩展开发 第 11.01 课:使用构建和发布 REST APIs,第 11.02 课:使用 Azure 管道 CLI,第 11.03 课:开发和分发扩展,摘要, 在前几章,我们讨论了 Azure DevOps 中的构建和发布管道。我们已经研究了经
    Apache CN

  • Azure ppl流水线-十二、将测试集成到管道中 第 12.01 课:使用管道运行单元测试,第 12.02 课:使用管道运行功能测试,摘要, 测试是软件交付过程中非常重要的一个方面。为了保证交付的软件项目或产品的质量,一些测试类型可以很容易地自动化,并与构建和发布管道相集成。在这一
    Apache CN

  • 五、创建构建管道——经典——变量、触发器、过滤器、选项和保留 第 5.01 课:使用变量,第 5.02 课:设置触发器和路径过滤器,第 5.03 课:格式化内部版本号,第 5.04 课:启用、禁用和暂停生成,第 5.05 课:生成和工作项,第 5.06 课:构建状态徽章,第 5.07 课:其他构建
    Apache CN

  • 六、创建构建管道——经典——排队、调试、任务组、工件和导入/导出选项 第 6.01 课:排队生成和启用调试模式以获取更多诊断信息,第 6.02 课:在 PowerShell 脚本中设置变量值,第 6.03 课:在 PowerShell 中访问秘密变量值,第 6.04 课:在构建中使用身份验证令牌,第 6.
    Apache CN

  • 四、创建构建管道——经典——源代码管理、模板、作业和任务 第 4.01 课:使用源代码管理提供程序,第 4.02 课:使用模板,第 4.03 课:使用多重职务,第 4.04 课:使用任务,摘要, 构建管道允许您编译源代码,运行单元测试,并将您的代码发布为可部署的工件。在经典的构建管道中,您可以
    Apache CN

  • Azure ppl流水线-七、使用工件 第 7.01 课:发布构建工件,第 7.02 课:将工件打包并发布为 NuGet,第 7.03 课:在构建中使用 NuGet 包,摘要, 构建的工件或输出包含二进制文件和将软件部署到目标环境所需的支持文件。根据正在构建的项目类型,以及部
    Apache CN

  • Azure ppl流水线-三、设置池、部署组和代理 第 3.01 课:设置池和权限,第 3.02 课:向池中添加代理,第 3.03 课:设置部署组,第 3.04 课:向部署组添加目标,摘要, 在前两章中,我们讨论了持续集成和交付的概念,并简要探讨了 Azure Pipelines 的概念
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多