线程池面试连环炮,你能抗住几题?

简介: 最近有朋友在面试时,刚好被问到了线程池相关的问题,于是我想就抽时间整理了一些关于线程池的面试题来分享给大家。

大家好,我是三友~~

最近有朋友在面试时,刚好被问到了线程池相关的问题,于是我想就抽时间整理了一些关于线程池的面试题来分享给大家。

公众号:三友的java日记

1.为什么要用线程池,线程池有什么好处么?

线程池,简单的来说就是管理了一堆线程的地方,这里的线程在执行完任务之后不是进行销毁,而是进行阻塞等待继续获取任务,从而实现了线程的重复利用,降低了线程的创建和销毁的资源消耗。

线程池有哪些参数?

corePoolSize:线程池中用来工作的核心的线程数量。

maximumPoolSize:最大线程数,线程池允许创建的最大线程数。

keepAliveTime:超出 corePoolSize 后创建的线程存活时间(当超过核心线程数后,又没有线程任务执行,达到该存活时间后,停止该线程)。

unit:keepAliveTime 的时间单位。

workQueue:任务队列,是一个阻塞队列,当线程数已达到核心线程数,会调用该方法来存储任务。

threadFactory :线程池内部创建线程所用的工厂。

handler:拒绝策略;当队列已满并且线程数量达到最大值时,会调用该方法处理该任务。

线程池的运行原理是什么?

如图:

image.png

1)线程池刚创建时,里面没有一个线程。

2)当调用 execute() 方法添加一个任务时,线程池会做如下判断:

  • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务
  • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
  • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
  • 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会拒绝这个任务,调用RejectedExecutionHandler 来对被拒绝掉的任务进行处理。

3)当一个线程完成任务时,它会从阻塞队列中来尝试获取下一个任务来执行,如果没有获取到,那么线程就会进入阻塞状态。

4)当一个线程超过一定的时间(keepAliveTime)时没有获取到任务的时候,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉,退出。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

5)如果设置了允许核心线程数超时,那么核心线程也会退出。

核心线程能否退出?

答案是可以的,可以通过方法allowCoreThreadTimeOut来让核心线程超时退出

拒绝策略有哪些?

jdk自带的有四种

  • AbortPolicy:丢弃任务,抛出运行时异常。
  • CallerRunsPolicy:有提交任务的线程来执行任务。
  • DiscardPolicy:丢弃这个任务,但是不抛异常
  • DiscardOldestPolicy:从队列中剔除最先进入队列的任务,然后再次提交任务

当然我们可以自己实现 RejectedExecutionHandler 接口来处理被拒绝的任务。

JDK自带的线程池种类有哪些?

1)固定线程数量的线程池:核心线程数与最大线程数相等

public static ExecutorService newFixedThreadPool(int nThreads) {
   
   
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
AI 代码解读

2)单个线程数量的线程池

public static ExecutorService newSingleThreadExecutor() {
   
   
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
AI 代码解读

3)接近无限大线程数量的线程池

public static ExecutorService newCachedThreadPool() {
   
   
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
AI 代码解读

4)带定时调度功能的线程池

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
   
   
        return new ScheduledThreadPoolExecutor(corePoolSize);
}
AI 代码解读

为什么不推荐使用JDK自带的线程池?

从上面的线程池的种类可以看出,newFixedThreadPool线程池,由于使用了LinkedBlockingQueue,队列的容量默认是无限大,实际使用中出现任务过多时导致内存溢出,newCachedThreadPool线程池由于核心线程数无限大,当任务过多的时候,会导致创建大量的线程,可能机器负载过高,导致服务宕机。

如何合理设置核心线程数的大小?

核心线程数的设置主要取决于业务是IO密集型还是CPU密集型。

CPU密集型指的是任务主要使用来进行大量的计算,没有什么导致的线程阻塞。一般这种场景的线程池核心线程数设置为CPU核心数+1。

IO密集型:当执行任务需要大量的io,比如磁盘io,网络io,可能会存在大量的阻塞,所以在IO密集型任务中使用多线程可以大大地加速任务的处理。一般核心线程数设置为 2*CPU核心数

java中用来获取CPU核心数的方法是:Runtime.getRuntime().availableProcessors();

说说submit和 execute两个方法有什么区别?

submit() 和 execute() 都可以往线程池中提交任务,区别是使用 execute() 执行任务无法获取到任务执行的返回值,而使用 submit()方法, 可以使用 Future 来获取任务执行的返回值。

shutdownNow() 和 shutdown() 两个方法有什么区别?

shutdownNow() 和 shutdown() 都是用来终止线程池的,它们的区别是,使用 shutdown() 程序不会报错,也不会立即终止线程,它会等待线程池中的缓存任务执行完之后再退出,执行了 shutdown() 之后就不能给线程池添加新任务了;shutdownNow() 会试图立马停止任务,线程中的任务不会再执行,也无法添加新的任务。

调用了shutdownNow或者shutdown,线程一定会退出么?

这个是不一定的,因为线程池会调用线程的interrupt()来打断线程的执行,但是这个方法不会打断正在运行的线程,只对正在阻塞等待的线程生效,一旦线程执行的任务类似于一个死循环,那么任务永远不会执行完,那么线程永远都不会退出。

为什么线程池要使用阻塞队列?

因为线程一旦任务执行完之后,如果想让线程不退出,只能阻塞或者自旋来保证线程不会退出,阻塞会让cpu资源,但是自旋不会,所以为了防止线程退出和减少cpu的消耗,选择使用阻塞队列来保证线程不会退出。

PS:如果觉得这篇文章对你有帮助,欢迎大家关注公众号三友的java日记、分享、点赞、在看,感谢支持。

往期热门文章推荐

如何去阅读源码,我总结了18条心法

如何写出漂亮代码,我总结了45个小技巧

三万字盘点Spring/Boot的那些常用扩展点

三万字盘点Spring 9大核心基础功能

万字+20张图剖析Spring启动时12个核心步骤

1.5万字+30张图盘点索引常见的11个知识点

两万字盘点那些被玩烂了的设计模式

搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习,回复 面试 即可获得一套面试真题。

目录
打赏
0
1
1
0
33
分享
相关文章
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
40 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
79 14
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
58 6
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
88 16
面试直击:并发编程三要素+线程安全全攻略!
并发编程三要素为原子性、可见性和有序性,确保多线程操作的一致性和安全性。Java 中通过 `synchronized`、`Lock`、`volatile`、原子类和线程安全集合等机制保障线程安全。掌握这些概念和工具,能有效解决并发问题,编写高效稳定的多线程程序。
81 11
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
45 6
Java 多线程 面试题
Java 多线程 相关基础面试题
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法

热门文章

最新文章