常见八股面试题:线程池核心线程会被销毁吗?

大家好,我是鸭鸭!

此答案节选自鸭鸭最近弄的面试刷题神器面试鸭,更多大厂常问面试题,可以点击进行阅读哈!

目前这个面试刷题神器刚出,有网站和小程序双端可用!


回答重点

答案是默认不会被销毁,但是可以通过配置 allowCoreThreadTimeOut 参数为 true 使得核心线程空闲后也会被回收!

顺着这题,我们再来过一遍如何回答线程池原理这道面试题。

首先,简述线程池的作用:线程池是一种池化技术,用于预先创建并管理一组线程,避免频繁创建和销毁线程的开销,提高性能和响应速度。

然后,简单带一下线程池的几个关键的配置:核心线程数、最大线程数、空闲存活时间、工作队列、拒绝策略。

在这里插入图片描述

最后,简述一下线程池的工作原理,按照下面的顺序来回答即可:

  1. 默认情况下线程不会预创建,任务提交之后才会创建线程(不过设置 prestartAllCoreThreads 可以预创建核心线程)。
  2. 当核心线程满了之后不会新建线程,而是把任务堆积到工作队列中。
  3. 如果工作队列放不下了,然后才会新增线程,直至达到最大线程数。
  4. 如果工作队列满了,然后也已经达到最大线程数了,这时候来任务会执行拒绝策略。
  5. 如果线程空闲时间超过空闲存活时间,并且线程线程数是大于核心线程数的则会销毁线程,直到线程数等于核心线程数(设置 allowCoreThreadTimeOut 为 true 可以回收核心线程,默认为 false)。

任务提交,线程池线程数还未达到核心线程数:
在这里插入图片描述

核心线程数已满,任务队列未满的情况:

在这里插入图片描述

核心线程数已满,任务队列已满的情况:
在这里插入图片描述

线程池中线程数已达最大线程数的情况:
在这里插入图片描述

注意,核心线程和非核心线程在线程池中是一样的,并没有特殊的标识区分!图中区分仅为说清创建的顺序

prestartAllCoreThreads 源码

线程池初始化会执行以下代码,默认的 prestartAllCoreThreads 为 false,因此默认不会创建核心线程。
在这里插入图片描述

不过,可以通过 setPrestartAllCoreThreads 将其改为 true。

在这里插入图片描述

processWorkerExit 源码

可以看到,根据 allowCoreThreadTimeOut 参数,实际可以控制线程池的最小线程数,使得核心线程数也可以被销毁。
在这里插入图片描述

扩展:线程池相关参数解释

  • corePoolSize:核心线程数,即线程池中始终保持的线程数量。
  • maximumPoolSize:最大线程数,即线程池中允许的最大线程数量。
  • keepAliveTime:线程空闲时间,超过这个时间的非核心线程会被销毁。
  • workQueue:任务队列,存放待执行的任务。
  • threadFactory:线程工厂,用于创建新线程。
  • rejectedExecutionHandler:任务拒绝处理器,当任务无法执行时的处理策略。

扩展:工作队列类型

  • SynchronousQueue:不存储任务,直接将任务提交给线程。
  • LinkedBlockingQueue:链表结构的阻塞队列,大小无限。
  • ArrayBlockingQueue:数组结构的有界阻塞队列。
  • PriorityBlockingQueue:带优先级的无界阻塞队列。

扩展:提供的线程池类型

Java 并发库中提供了哪些线程池实现?它们有什么区别?

扩展:线程池拒绝策略

Java 线程池有哪些拒绝策略?

最后

最后再推荐下鸭鸭目前努力在做面试刷题神器面试鸭,已经有 3000 多道面试题目啦,欢迎大家来阅读!如果大家有不会的面试题,也可以在面试鸭内反馈!鸭鸭会第一时间为大家解答! 我是鸭鸭,我们下期见~

### Java线程常见面试题解析 #### 1. 如何确保多个线程按特定顺序执行? 为了确保三个线程 T1, T2 和 T3 按照指定顺序执行,可以使用 `join()` 方法或者手动锁机制。`join()` 方法可以让当前线程等待另一个线程完成后再继续执行[^1]。 以下是通过 `join()` 实现的代码示例: ```java public class OrderedThreads { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> System.out.println("T1 is running")); Thread t2 = new Thread(() -> { try { t1.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("T2 is running"); }); Thread t3 = new Thread(() -> { try { t2.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("T3 is running"); }); t1.start(); t2.start(); t3.start(); // Ensure the main thread waits for all threads to finish. t1.join(); t2.join(); t3.join(); System.out.println("Main thread finished."); } } ``` 另一种方式是使用显式的同步工具如 `CountDownLatch` 或者 `ReentrantLock` 来控制线程间的执行次序[^4]。 --- #### 2. 线程池中的核心参数有哪些?其作用是什么? 线程池核心参数包括以下几个方面: - **corePoolSize**: 表示线程池中保持存活的最小线程数。 - **maximumPoolSize**: 表示线程池允许的最大线程数。 - **keepAliveTime**: 如果当前线程数超过了 corePoolSize,则多余的空闲线程会在 keepAliveTime 时间后被销毁。 - **workQueue**: 存放待处理任务的阻塞队列。 当提交的任务数量超过最大线程数且队列已满时,线程池会拒绝新任务并抛出 `RejectedExecutionException` 异常[^2]。 --- #### 3. 启动自定义线程的方式有哪些? 启动一个自定义线程可以通过继承 `Thread` 类或实现 `Runnable` 接口两种主要方式。推荐使用后者,因为它不会占用额外的类层次结构空间[^3]。 下面是一个基于 `Runnable` 的例子: ```java class MyTask implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is executing..."); } } public class TaskRunner { public static void main(String[] args) { MyTask task = new MyTask(); Thread thread = new Thread(task); thread.start(); } } ``` --- #### 4. 如何在线程间安全地共享数据? 在 Java 中,可以在两个线程之间共享数据的方法有很多种,比如使用 `volatile` 关键字、`synchronized` 块/方法以及高级并发工具(如 `AtomicInteger`, `ConcurrentHashMap`)。对于更复杂的场景,还可以考虑使用 `BlockingQueue` 或者显示锁 `ReentrantLock`。 以下是一个简单的 `synchronized` 示例: ```java class SharedResource { private int value; public synchronized void increment() { this.value++; System.out.println(Thread.currentThread().getName() + ": Value incremented to " + value); } public synchronized int getValue() { return value; } } public class DataSharingExample { public static void main(String[] args) { final SharedResource resource = new SharedResource(); Thread producer = new Thread(() -> { for(int i=0; i<5; ++i){ resource.increment(); } }, "Producer"); Thread consumer = new Thread(() -> { while(resource.getValue() < 5){ try{ Thread.sleep(100); }catch(Exception ex){} } System.out.println("Consumer: Final value reached "+resource.getValue()); }, "Consumer"); producer.start(); consumer.start(); } } ``` --- #### 5. 死锁是如何产生的?如何预防死锁? 死锁通常发生在多个线程互相持有对方所需的资源而无法继续运行的情况下。要防止死锁的发生,可以采取以下措施之一:按照固定的顺序获取锁;避免嵌套锁定操作;设置超时时间等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值