计算机系统应用教程网站

网站首页 > 技术文章 正文

Java 线程池ThreadPoolExecutor

btikc 2024-11-21 10:54:42 技术文章 48 ℃ 0 评论

在JDK中,ThreadPoolExecutor是线程池的具体实现。这个类提供了线程池所需的各种方法,包括任务提交、线程管理、监控等核心功能。基于它,我们可以进一步扩展业务功能,例如实现定时任务的ScheduledThreadPoolExecutor就是ThreadPoolExecutor的子类。尽管如此,这不是本文的重点。下面,我们将深入探讨其源码。

首先,理解线程池实现中的几个核心概念和流程是关键。

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

在多线程编程中,一个重要的概念是submit方法。这个方法接收RunnableCallable类型的参数,它们分别代表了要执行的任务。值得注意的是,这个参数并不是用于创建新线程并立即启动的。实际上,这个参数所定义的任务是在线程池中的某个线程上执行的。具体来说,当线程池中的线程空闲时,它会从任务队列中取出任务并执行,这就是所谓的任务调度。因此,我们不能简单地将submit方法的参数理解为用于创建和启动新线程的代码块,而应该理解为定义了在线程上执行的任务。

对于初学者来说,使用Executors工具类来快速创建线程池是非常方便的。通过这个工具类,开发者可以简化线程池的创建过程,而不需要过多关注细节。只需要提供必要的参数,其他参数都可以采用默认值。这些默认值最终都会导向线程池的构造方法,从而实现线程池的创建。因此,使用Executors工具类可以帮助开发者更高效地创建和管理线程池,从而更好地利用多线程资源。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        // 这几个参数都是必须要有的
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
      
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

基本上,下面是一些常见的参数和它们的含义:

  1. corePoolSize(核心线程数):
    这是线程池中的基本大小,即即使在空闲时间也不会被销毁的线程数(除非设置了allowCoreThreadTimeOut)。当提交一个新任务到线程池时,如果当前线程数小于corePoolSize,即使其他空闲线程能够执行新任务,线程池也会创建一个新线程来处理任务。
  2. maximumPoolSize(最大线程数):
    线程池允许的最大线程数。当队列满了,并且已经创建的线程数达到这个数,线程池就不会再创建新线程。但在某些策略下,如使用无界队列或者任务拒绝策略时,这个参数可能不会有太大影响。
  3. keepAliveTime(保持活跃时间):
    当线程数超过corePoolSize时,这是多余的空闲线程在被终止之前等待新任务的最长时间。这个时间单位由unit参数指定,通常是毫秒。
  4. unit(时间单位):
    keepAliveTime参数的时间单位,它是一个TimeUnit枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)等。
  5. workQueue(工作队列):
    用于存储等待执行的任务的队列。这个队列必须是一个阻塞队列,通常有几种选择,如ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。队列的选择会影响线程池的行为和性能。
  6. threadFactory(线程工厂):
    用于创建新线程的工厂。你可以通过提供自定义的线程工厂来给新创建的线程设置有意义的名字、优先级、守护状态等。
  7. handler(拒绝策略):
    当工作队列和线程池都满了,无法再接受新任务时,这个策略会被用来处理这些无法执行的任务。有几种预定义的策略,如AbortPolicy(默认,抛出异常)、CallerRunsPolicy(调用者自己执行任务)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃队列中最旧的任务)等,或者你可以实现RejectedExecutionHandler接口来提供自定义策略。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表