Java并发编程 - 线程池ThreadPoolExecutor详解

在Java并发编程中,ThreadPoolExecutorjava.util.concurrent包中提供的一个非常重要的线程池实现。它允许开发者控制线程池的行为,如核心线程数、最大线程数、空闲线程存活时间等,并提供了多种拒绝策略来处理超出容量的任务。

ThreadPoolExecutor的基本概念

核心参数
  • corePoolSize:线程池的核心线程数。即使没有任务执行,这些线程也会一直存活(除非它们的空闲时间超过了keepAliveTime)。
  • maximumPoolSize:线程池的最大线程数。当任务数量超过核心线程数时,新创建的线程数不会超过这个值。
  • keepAliveTime:当线程数大于核心线程数时,多余的线程空闲多久后会被销毁。只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。
  • unitkeepAliveTime的时间单位。
  • workQueue:用来存放任务的工作队列。当提交的任务数量超过核心线程数时,额外的任务会暂时存储在这个队列中等待执行。
  • threadFactory:创建新线程的工厂。可以通过它定制线程的创建方式。
  • handler:拒绝策略,当任务数量超过最大限制时(即队列已满且线程数达到maximumPoolSize),如何处理新来的任务。
主要方法
  • execute(Runnable command):执行给定的任务。
  • submit(Callable task):提交一个可返回结果的任务,并返回一个Future<T>对象,该对象可用于获取任务的结果。
  • submit(Runnable task, T result):提交一个任务,并返回一个Future<T>对象,该对象可用于获取任务的结果。
  • shutdown():停止接收新任务,但继续执行已经提交的任务。
  • shutdownNow():立即终止所有正在执行的任务,并返回一个包含尚未执行的任务列表。

创建ThreadPoolExecutor

创建一个ThreadPoolExecutor实例通常需要指定上面提到的一些参数。例如,以下代码创建了一个线程池,它有2个核心线程,最大线程数为5,使用LinkedBlockingQueue作为工作队列,并定义了一个简单的拒绝策略:

import java.util.concurrent.*;

public class ThreadPoolExecutorExample {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, // corePoolSize
                5, // maximumPoolSize
                60, // keepAliveTime
                TimeUnit.SECONDS, // unit
                new LinkedBlockingQueue<>(10), // workQueue
                Executors.defaultThreadFactory(), // threadFactory
                new ThreadPoolExecutor.AbortPolicy() // handler
        );

        // 提交任务
        for (int i = 0; i < 15; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Task ID: " + taskId + " is running by " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟耗时操作
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

拒绝策略

当线程池无法接受新任务时,可以采用不同的拒绝策略来处理这些任务:

  1. AbortPolicy:抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:调用者的线程直接执行任务。
  3. DiscardOldestPolicy:丢弃队列中最老的任务,并尝试再次提交新任务。
  4. DiscardPolicy:简单地丢弃任务而不做任何通知。

线程池状态

ThreadPoolExecutor有以下几种状态:

  • RUNNING:接受新任务,并处理等待队列中的任务。
  • SHUTDOWN:不再接受新任务,但会继续处理等待队列中的任务。
  • STOP:不再接受新任务,并中断正在执行的任务。
  • TIDYING:所有任务完成,线程池进入收尾状态。
  • TERMINATED:收尾完成后进入的状态。

线程池生命周期

线程池的生命周期大致如下:

  1. 创建线程池。
  2. 当提交任务时,首先检查当前线程数是否小于corePoolSize,如果是,则创建新线程来执行任务。
  3. 如果当前线程数等于corePoolSize并且还有任务未处理,则将任务放入workQueue
  4. 如果workQueue已满,则创建新线程(不超过maximumPoolSize)来执行任务。
  5. 如果当前线程数达到maximumPoolSize并且workQueue已满,则根据拒绝策略处理新任务。
  6. 当线程池处于SHUTDOWN状态时,不再接受新任务,但会继续处理等待队列中的任务。
  7. 当线程池处于STOP状态时,不再接受新任务,并中断正在执行的任务。
  8. 当所有任务完成并且线程池处于SHUTDOWN状态时,线程池最终进入TERMINATED状态。

使用场景

  • 短期任务:当任务执行时间较短时,可以使用较小的核心线程数和较大的工作队列。
  • 长期任务:对于执行时间较长的任务,可能需要较大的核心线程数,以避免大量线程长时间处于空闲状态。
  • 固定线程数:当任务数量相对稳定时,可以设置核心线程数等于最大线程数,这样线程池中的线程数量就固定不变了。
  • 周期性任务:可以使用定时任务框架,如ScheduledThreadPoolExecutor,它也是基于ThreadPoolExecutor的。

总结

ThreadPoolExecutor是Java中实现线程池的一种强大工具,它可以灵活地配置线程池的各种参数,以适应不同的应用场景。合理配置线程池可以显著提高系统的响应速度和吞吐量,同时减少资源消耗。在设计线程池时,应该根据具体的需求仔细考虑各种参数的选择,以确保线程池能够高效稳定地运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值