线程池是 Java 并发编程中非常重要的一个概念,以下是线程池常见的面试题及答案:
-
什么是线程池? 线程池是一种线程管理机制,它在程序启动时就创建一定数量的线程,并将它们放到一个池子里面,当需要执行任务时,就从线程池中取出一个线程来执行任务,执行完任务后,该线程并不会被销毁,而是继续留在池子里面等待下一次任务的执行。
-
线程池的好处是什么? 线程池的好处主要有以下几点:
- 线程池可以避免线程频繁创建和销毁的开销,提高程序的性能。
- 线程池可以控制并发的数量,避免系统资源被过度占用,避免系统崩溃。
- 线程池可以提高代码的可读性和可维护性,因为它将线程的创建、销毁、执行等操作封装起来,使得代码更加简洁、清晰。
3.线程池有哪些常见的实现方式? Java 中提供了四种线程池的实现方式:
- newCachedThreadPool:缓存线程池,适用于执行大量的短期异步任务。
- newFixedThreadPool:固定大小的线程池,适用于执行数量确定的长期任务。
- newScheduledThreadPool:定时执行任务的线程池,适用于周期性执行任务。
- newSingleThreadExecutor:单个线程的线程池,适用于按顺序执行任务的场景。
4.线程池中的核心线程数、最大线程数、队列等参数的作用是什么?
线程池中的核心线程数是线程池的初始大小,最大线程数是线程池能够容纳的最大线程数量。当任务数量超过核心线程数时,线程池会将任务放到队列中等待执行,当队列已满时,线程池会创建新的线程来执行任务,当达到最大线程数时,线程池不再创建新的线程。队列的作用是保存等待执行的任务,不同的队列实现方式有不同的特点,比如 ArrayBlockingQueue 是一个基于数组的有界阻塞队列,可以避免线程池中任务的过度增加。
5.线程池中的任务执行顺序是什么样的?
线程池中的任务执行顺序与队列的类型有关,如果是有界队列,任务执行顺序是按照任务提交的顺序执行的,如果是无界队列,任务执行顺序是不确定的。
6.如何避免线程池中的内存泄漏? 线程池中的内存泄漏主要是由于线程池中的线程长时间不被使用,导致线程未被正确释放。可以通过以下几种方式来避免线程池中的内存泄漏:
- 设置合适的线程池参数,比如核心线程数、最大线程数、队列容量等。
- 及时关闭线程池,不再使用的线程池应该及时关闭,以释放资源。
- 使用合适的队列,比如 ArrayBlockingQueue 等有界队列,可以避免线程池中任务的过度增加。
7.线程池的执行过程是怎样的? 线程池的执行过程大致如下:
- 线程池启动时,会创建一定数量的线程,加入到线程池中,处于空闲状态。
- 当有任务提交时,线程池会从线程池中选择一个线程来执行任务,如果所有线程都在忙碌,则任务被加入到队列中等待执行。
- 当队列已满时,如果线程池的线程数还没有达到最大值,则会创建新的线程执行任务,如果线程数已经达到最大值,则会采取一定的策略处理任务,比如抛出异常或者拒绝任务。
- 当线程执行完任务后,线程不会被销毁,而是继续留在线程池中等待下一次任务的执行。
8.线程池中的线程是如何复用的? 线程池中的线程复用主要是通过线程池的线程复用机制来实现的,即线程池中的线程在执行完任务后,并不会被销毁,而是继续留在线程池中等待下一次任务的执行。这种方式避免了线程频繁创建和销毁的开销,提高了程序的性能。
9.线程池的拒绝策略有哪些? 线程池的拒绝策略主要有以下四种:
- AbortPolicy(默认):直接抛出 RejectedExecutionException 异常,不执行任务。
- CallerRunsPolicy:将任务交给提交任务的线程来执行,即让提交任务的线程自己执行该任务。
- DiscardOldestPolicy:丢弃队列中最旧的任务,然后重新尝试执行任务。
- DiscardPolicy:直接丢弃任务,不做任何处理。