MST

星途 面试题库

面试题:Java可缓存线程池与其他类型线程池的深度对比及应用场景选择

请深入对比Java可缓存线程池与FixedThreadPool、SingleThreadExecutor等其他类型线程池在工作原理、资源管理、适用场景等方面的差异。并举例说明在不同的业务需求下,如何合理选择使用哪种线程池。
47.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 工作原理

  • 可缓存线程池(CachedThreadPool)
    • 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
    • 如果线程池里所有线程都在执行任务,又有新任务到来,会创建新线程处理任务。
    • 线程池中的线程如果60秒内未被使用就会被回收。
  • FixedThreadPool
    • 线程池大小固定,在创建后,线程数量不会改变。
    • 当有新任务提交时,如果线程池中有空闲线程,则立即执行任务;如果没有空闲线程,任务会被放入阻塞队列等待。
  • SingleThreadExecutor
    • 只有一个线程的线程池,它确保所有任务按照提交顺序依次执行。
    • 所有任务会被放入一个队列中,由这唯一的线程按顺序逐个执行。

2. 资源管理

  • 可缓存线程池(CachedThreadPool)
    • 资源消耗不固定,可能会在短时间内创建大量线程,从而消耗过多内存等资源,如果任务执行时间较长,会导致线程数量持续增长,可能引发OOM(OutOfMemoryError)。
    • 适用于处理大量短时间任务,线程复用率高,在任务执行完后,空闲线程会被回收,释放资源。
  • FixedThreadPool
    • 资源消耗相对稳定,因为线程数量固定。
    • 线程创建后一直存在,即使无任务执行也不会销毁,占用一定系统资源。
  • SingleThreadExecutor
    • 资源消耗最少,只有一个线程在运行,占用的线程资源恒定。
    • 所有任务排队等待执行,不会因为线程过多而消耗过多资源。

3. 适用场景

  • 可缓存线程池(CachedThreadPool)
    • 适用于执行大量的短时间异步任务,例如网页爬虫场景,需要快速发起大量HTTP请求获取网页数据,每个请求任务执行时间较短,CachedThreadPool能快速创建和复用线程,提高效率。
    • 不适合长时间运行的任务,否则线程不断创建可能耗尽资源。
  • FixedThreadPool
    • 适用于处理负载相对稳定,且任务执行时间较长的场景。例如在电商系统中,订单处理任务,需要进行一系列复杂的业务逻辑处理,且处理频率相对稳定,使用FixedThreadPool能保证系统资源的合理利用,避免过多线程创建销毁带来的开销。
    • 可控制并发度,防止因并发过高导致系统资源耗尽。
  • SingleThreadExecutor
    • 适用于需要顺序执行任务,且不希望有并发干扰的场景。比如数据库的事务操作,要求按顺序执行,避免并发操作导致数据一致性问题,使用SingleThreadExecutor可以保证任务顺序执行。

4. 业务需求下的选择示例

  • 需求一:处理大量短时间的异步任务
    • 比如图片的缩略图生成,当用户上传图片后,系统需要快速生成不同尺寸的缩略图。每个缩略图生成任务执行时间较短,但数量可能较多。
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < 100; i++) {
        executorService.submit(() -> {
            // 生成缩略图的业务逻辑
            System.out.println("生成缩略图任务 " + Thread.currentThread().getName());
        });
    }
    executorService.shutdown();
    
  • 需求二:处理负载稳定且执行时间长的任务
    • 假设是一个视频转码系统,接收到用户上传的视频后,需要将其转码为不同格式。转码任务执行时间较长且系统接收任务频率相对稳定。
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 10; i++) {
        executorService.submit(() -> {
            // 视频转码的业务逻辑
            System.out.println("视频转码任务 " + Thread.currentThread().getName());
        });
    }
    executorService.shutdown();
    
  • 需求三:需要顺序执行且无并发干扰的任务
    • 以银行转账操作为例,需要保证每笔转账操作按顺序依次执行,防止并发导致账户余额计算错误。
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 5; i++) {
        executorService.submit(() -> {
            // 银行转账的业务逻辑
            System.out.println("银行转账任务 " + Thread.currentThread().getName());
        });
    }
    executorService.shutdown();