线程池面试题合集

1. 什么是线程池?

线程池是一种管理和复用线程的机制,它可以减少线程的创建和销毁次数,避免线程频繁地创建和销毁对系统性能带来的负面影响。线程池通过维护一定数量的线程,将任务提交给空闲的线程来执行,从而提高了系统的并发处理能力。

2. 什么是Executor框架,如何使用它创建线程池?

Executor框架是Java SE5中引入的一组API,它提供了一种标准的方式来创建和管理线程池。使用Executor框架创建线程池可以简化线程池的管理和使用,提高线程池的可复用性和可维护性。

下面是使用Executor框架创建线程池的示例代码:

ExecutorService executorService = Executors.newFixedThreadPool(10);

上述代码创建一个固定大小的线程池,线程池中最多有10个线程同时执行任务。Executor框架还提供了其他的创建线程池的方法,如newCachedThreadPool()newSingleThreadExecutor()等。

3. 四大快捷创建线程池的方法和详细说明

  • newFixedThreadPool(int nThreads):创建一个固定大小的线程池,线程池中的线程数量固定不变。当线程池中所有线程都被占用时,新的任务会被放入等待队列。
  • newCachedThreadPool():创建一个可缓存的线程池,线程池中的线程数量不固定,会根据任务的多少自动调整线程池中的线程数量。空闲线程会被保留60秒,并且在需要时重新利用这些线程。
  • newSingleThreadExecutor():创建一个只有一个线程的线程池,所有任务都将在该线程中执行。如果该线程因为异常退出,那么会创建一个新的线程来替代它。
  • newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,可以执行定时任务和周期性任务。

4. 为什么不建议使用Executor直接创建线程池?

虽然Executor提供了便捷的方法来创建线程池,但是直接使用Executor创建线程池也有一些缺点。主要有以下两个方面:

阿里巴巴Java开发手册,明确指出不允许使用Executors静态工厂构建线程池,原因如下:

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

说明:Executors返回的线程池对象的弊端如下:

1:FixedThreadPool 和 SingleThreadPool:
允许的请求队列(底层实现是LinkedBlockingQueue)长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2:CachedThreadPool 和 ScheduledThreadPool
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

5. 使用ThreadPoolExecutor创建线程池的参数详细说明

ThreadPoolExecutor是Java提供的一个灵活的线程池实现类,可以通过它来自定义线程池的各种参数。下面是ThreadPoolExecutor的构造函数的参数说明:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
        BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
  • corePoolSize:线程池中的核心线程数,当线程池中的线程数量小于这个值时,新的任务会创建新的线程。
  • maximumPoolSize:线程池中的最大线程数,当线程池中的线程数量达到这个值时,新的任务会被放入等待队列。
  • keepAliveTime:线程空闲时间,当线程的空闲时间超过这个值时,多余的线程会被销毁。
  • unit:时间单位,用来指定keepAliveTime的时间单位,如秒、毫秒等。
  • workQueue:工作队列,用来保存等待执行的任务,ThreadPoolExecutor提供了多种工作队列实现,如LinkedBlockingQueue、ArrayBlockingQueue等。
  • threadFactory:线程工厂,用来创建新的线程。
  • handler:拒绝策略,用来处理无法处理的任务,ThreadPoolExecutor提供了多种拒绝策略实现,如AbortPolicy、CallerRunsPolicy等。

6. 线程池的关闭

线程池的关闭是一个比较重要的问题,如果不正确地关闭线程池,会导致一些任务没有被执行完或者线程池无法释放已经占用的资源。ThreadPoolExecutor提供了两种关闭线程池的方法:

  • shutdown():该方法会平缓地关闭线程池,在调用该方法之后,线程池不再接受新的任务,但是会执行完所有已经提交的任务,然后关闭线程池。调用该方法后,线程池会一直等待所有任务执行完毕才会关闭。
  • shutdownNow():该方法会立即关闭线程池,它会尝试停止所有正在执行的任务,并且不再处理等待队列中的任务。调用该方法后,线程池会立即释放所有已经占用的资源。

7. 初始化线程池时线程数如何设定?

线程池的初始化线程数是一个比较重要的参数,它会直接影响到线程池的性能和吞吐量。通常情况下,初始化线程数的设定需要根据实际的业务需求和系统性能来进行调整。

如果系统中有大量的CPU密集型任务,那么可以将初始化线程数设置为CPU核心数的22倍,这样可以提高IO操作的并发处理能力。

8. 线程池常用的工作队列说明

线程池的工作队列是管理等待执行的任务的一个重要组成部分,常用的工作队列有以下几种:

  • LinkedBlockingQueue:一个基于链表的阻塞队列,可以无限地添加元素,当队列满时会阻塞等待队列空闲。该队列比较适合用于固定大小的线程池中。
  • ArrayBlockingQueue:一个基于数组的阻塞队列,可以指定队列的容量,当队列满时会阻塞等待队列空闲。该队列比较适合用于固定大小的线程池中。
  • SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等待另一个线程的移除操作,反之亦然。该队列比较适合用于处理大量短暂的任务。
  • PriorityBlockingQueue:一个基于优先级的无限阻塞队列,可以按照元素的优先级顺序执行任务。该队列比较适合用于需要按照优先级顺序执行任务的场景。

9.线程池的执行流程

1. 初始化线程池

在初始化线程池时,我们需要指定线程池的大小、任务队列的大小等参数。同时,我们还需要创建一些线程,以便在任务到来时能够立即执行。

2. 提交任务

当我们有任务需要执行时,我们可以将任务提交到线程池中。这些任务会被加入到任务队列中,等待线程池中的线程来执行。

3. 线程执行任务

线程池中的线程会不断地从任务队列中获取任务,并执行这些任务。当任务队列中没有任务时,线程可能会等待,直到有新的任务到来。

4. 任务执行完毕

当一个任务执行完毕时,线程会将执行结果返回给调用方,并继续执行下一个任务。如果线程池中的线程数量是有限制的,那么当所有的线程都在执行任务时,新的任务会被加入到任务队列中等待。

5. 关闭线程池

当我们不再需要线程池时,我们需要将线程池关闭。在关闭线程池时,我们需要等待所有的任务执行完毕,并停止所有的线程。当线程池被关闭之后,我们就不能再向其中提交任务了。

以上就是线程池的执行流程。线程池可以提高程序的效率,减少线程创建和销毁的开销,同时还可以控制线程的数量,避免线程数量过多造成的问题。

06-02 20:59