主要线程对比
Java 提供了多种线程池,主要通过 Executors
工具类和 ThreadPoolExecutor
自定义实现线程池。下面对常见的 5 种线程池类型 进行详细对比,最多使用的还是自定义线程池。
1. newCachedThreadPool
(缓存线程池)
- 特点:
- 线程数不固定,根据任务数量动态创建线程。
- 空闲线程存活时间为 60 秒,超过时间会被回收。
- 如果线程可用,复用现有线程;否则创建新线程。
- 适用场景:
- 任务执行时间短,任务数量不确定的场景。
- 风险:
- 线程数量无限制,任务过多时可能导致内存溢出(OOM)。
示例:
ExecutorService executor = Executors.newCachedThreadPool();
2. newFixedThreadPool
(固定大小线程池)
- 特点:
- 线程池的线程数是固定的,不会动态增加或减少。
- 超出线程数的任务会进入阻塞队列等待执行。
- 线程不会被回收,适合长期运行的任务。
- 适用场景:
- 需要控制并发线程数,任务量较大且稳定的场景。
- 风险:
- 队列过长可能导致任务堆积,影响响应速度。
示例:
ExecutorService executor = Executors.newFixedThreadPool(10);
3. newSingleThreadExecutor
(单线程线程池)
- 特点:
- 线程池只有一个线程,所有任务按顺序执行(FIFO)。
- 保证任务按顺序执行,线程异常时会创建新线程替代。
- 适用场景:
- 需要保证任务顺序执行的场景。
- 适用于单线程环境,避免多线程问题。
- 风险:
- 任务过多会导致执行时间过长,影响性能。
示例:
ExecutorService executor = Executors.newSingleThreadExecutor();
4. newScheduledThreadPool
(定时任务线程池)
- 特点:
- 线程池支持定时任务和周期性任务执行。
- 核心线程数固定,非核心线程会被回收。
- 适用场景:
- 需要执行定时任务、周期任务的场景,如日志备份、定时调度等。
- 风险:
- 如果任务执行时间超过周期时间,可能会导致任务堆积。
示例:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); // 定时执行任务 executor.schedule(() -> System.out.println("任务执行"), 5, TimeUnit.SECONDS); // 周期性执行任务 executor.scheduleAtFixedRate(() -> System.out.println("周期任务"), 0, 10, TimeUnit.SECONDS);
5. newWorkStealingPool
(工作窃取线程池,Java 8+)
- 特点:
- 基于 ForkJoinPool 实现,使用并行处理任务,默认线程数为 CPU 核心数。
- 每个线程维护一个任务队列,当其他线程空闲时,可以窃取任务执行。
- 适用场景:
- 适合并行计算、任务较多且耗时不均匀的场景。
- 风险:
- 需要考虑任务的并行度和性能,适用于任务拆分的情况。
示例:
ExecutorService executor = Executors.newWorkStealingPool();
6. 自定义线程池(推荐)
- 特点:
- 使用
ThreadPoolExecutor
可以自定义核心参数,提供更高灵活性和控制力。
- 使用
- 参数说明:
- corePoolSize:核心线程数。
- maximumPoolSize:最大线程数。
- keepAliveTime:空闲线程存活时间。
- workQueue:任务队列。
- threadFactory:线程工厂,设置线程名称。
- handler:拒绝策略,任务无法执行时的处理方式。
- 适用场景:
- 需要精确控制线程池行为的场景。
示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() );