【揭秘】ThreadPoolExecutor全面解析

发布时间:2024年01月24日

【揭秘】ThreadPoolExecutor全面解析 - 程序员古德

内容摘要

ThreadPoolExecutor线程池通过资源复用、负载均衡、并发控制、异步处理和灵活扩展等方式,提高了系统性能、响应速度和满足了实际需求的调整。

核心概念

ThreadPoolExecutor是Java中强大的线程管理工具,它能够高效地复用线程,显著降低资源消耗与响应延迟,提升系统吞吐量,通过灵活配置核心与最大线程数、队列容量等参数,ThreadPoolExecutor可轻松应对不同并发场景,确保任务平稳运行。

ThreadPoolExecutor提供了对多线程更细粒度的控制,接下来模拟一个例子,假如,有一个电商平台,在双11这样的大促活动时,需要处理比平常多出数倍的订单,如果每个订单都单独用一个线程处理,那么系统很快就会因为线程过多而崩溃,这就好比一条拥堵不堪的道路,每辆车(线程)都在缓慢移动,整体效率极低。

ThreadPoolExecutor就像是一个高效的交通指挥中心,可以预设一定数量的“车道”(线程),这些“车道”可以并行处理多个订单(任务),当新的订单来临时,如果“车道”有空闲,就立即分配一个“车道”给这个订单;如果所有“车道”都在忙碌,新的订单就进入等待区,等待有“车道”空闲出来。

通过合理配置ThreadPoolExecutor的参数,比如核心线程数、最大线程数、队列容量等,可以让这个“交通指挥中心”更加智能和高效。比如,在订单高峰期,可以临时增加“车道”数量(线程数)以应对压力;在订单量较少的时候,则可以减少“车道”数量,以节省系统资源。

使用案例

下面是一个简单的ThreadPoolExecutor使用案例,如下代码:

import java.util.concurrent.ArrayBlockingQueue;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
  
public class ThreadPoolExecutorExample {  
  
    public static void main(String[] args) {  
        // 创建一个具有固定大小的线程池  
        int corePoolSize = 2; // 核心线程数  
        int maximumPoolSize = 4; // 最大线程数  
        long keepAliveTime = 10; // 空闲线程存活时间  
        ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2); // 任务队列  
  
        // 创建 ThreadPoolExecutor 实例  
        ThreadPoolExecutor executor = new ThreadPoolExecutor(  
                corePoolSize,  
                maximumPoolSize,  
                keepAliveTime,  
                TimeUnit.SECONDS,  
                queue  
        );  
  
        // 提交任务到线程池  
        for (int i = 0; i < 6; i++) {  
            final int taskId = i;  
            executor.submit(() -> {  
                System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());  
                try {  
                    // 模拟任务执行时间  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            });  
        }  
    }  
}

在上面的代码中,创建了一个ThreadPoolExecutor实例,设置了核心线程数为2,最大线程数为4,并且使用了一个容量为2的任务队列,然后提交了6个任务到线程池,由于任务队列和核心线程数之和(2 + 2)小于提交的任务数(6),因此一些任务需要等待,直到有线程变得可用。

执行这段代码,会输出如下内容:

Task 0 is running on thread pool-1-thread-1  
Task 1 is running on thread pool-1-thread-2  
Task 2 is running on thread pool-1-thread-1  
Task 3 is running on thread pool-1-thread-2  
Task 4 is running on thread pool-1-thread-1  
Task 5 is running on thread pool-1-thread-2

核心API

ThreadPoolExecutor 是 Java 并发库中的一个类,提供了丰富的方法来控制线程池的行为和管理提交的任务,线程池通过重用现有的线程来减少在创建和销毁线程上所需的时间以及系统资源的开销,以下是一些关键方法的概述:

1、关键构造方法

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

构造方法用于创建一个新的 ThreadPoolExecutor 实例,各个参数的含义是:

  • corePoolSize:核心线程数,即线程池中最小的线程数量,即使这些线程处于空闲状态,也不会被回收。
  • maximumPoolSize:线程池允许的最大线程数。
  • keepAliveTime:当线程数超过核心线程数时,多余空闲线程在终止前等待新任务的最长时间。
  • unitkeepAliveTime 的时间单位。
  • workQueue:用于存储等待执行的任务的阻塞队列。
  • threadFactory:用于创建新线程的工厂。
  • handler:当线程池无法执行新任务时(如队列已满或线程池已关闭),用于处理这些任务的策略。

2、关键方法

  1. execute(Runnable command)
    提交一个需要执行的任务,该方法没有返回值,通常用于执行不需要返回结果的任务。

  2. submit(Runnable task)submit(Callable task)
    提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 FutureRunnable 任务返回 null,而 Callable 任务返回其调用的结果。

  3. shutdown()
    启动线程池的关闭,这不会立即终止已提交的任务,但会拒绝所有新任务。

  4. shutdownNow()
    尝试停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

  5. isShutdown()
    如果线程池已关闭,则返回 true

  6. isTerminated()
    如果所有任务都已完成,线程池的工作线程也已关闭,则返回 true

  7. awaitTermination(long timeout, TimeUnit unit)
    请求关闭、发生超时或者当前线程被中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。

  8. getPoolSize()
    返回线程池中的线程数量。

  9. getActiveCount()
    返回正在执行任务的线程数量。

  10. getTaskCount()
    返回线程池大约执行过的任务总数。

  11. getCompletedTaskCount()
    返回线程池已完成的任务总数,该值小于或等于 getTaskCount() 方法返回的值。

  12. prestartAllCoreThreads()
    启动所有核心线程,使它们等待工作队列中的任务。

  13. prestartCoreThread()
    启动一个核心线程,如果还没有核心线程启动或者可以启动的话。

  14. allowsCoreThreadTimeOut()setAllowsCoreThreadTimeOut(boolean value)
    这些方法用于确定和设置核心线程是否会在空闲时超时。

  15. getCorePoolSize(), setMaximumPoolSize(int maximumPoolSize), getKeepAliveTime(TimeUnit unit), getThreadFactory(), getRejectedExecutionHandler() 等等:
    这些方法用于获取或设置线程池的各种配置参数。

注意,线程池的配置对其行为至关重要,配置不当可能会导致性能问题、资源耗尽或任务被拒绝,因此,在使用 ThreadPoolExecutor 时,务必仔细考虑这些配置参数。

核心总结

【揭秘】ScheduledThreadPoolExecutor全面解析 - 程序员古德

ThreadPoolExecutor提供了线程池的实现,其优点主要表现在:

  1. 资源复用:通过线程池,可以复用已存在的线程,避免频繁地创建和销毁线程,降低了开销。
  2. 提高性能:线程池中的线程是预先创建的,能快速响应请求,提高系统的吞吐量。
  3. 任务调度:ThreadPoolExecutor提供了多种调度策略,如优先队列、轮询等,以满足不同的任务需求。

它的缺点也是非常明显的,如下:

  1. 无法充分利用多核资源:如果线程数量设置得较少,可能无法充分利用多核CPU资源。
  2. 难以管理:如果线程池参数设置不当,如线程数过多,可能导致系统资源耗尽;反之,则可能无法充分发挥系统性能。

因此在使用时,需要根据实际需求和系统资源,设置合适的核心线程数,并为ThreadPoolExecutor设置合适的拒绝策略,以处理无法执行的任务。并且要定期监控线程池的状态,根据实际运行情况对线程池参数进行调优。

关注我,每天学习互联网编程技术 - 程序员古德

文章来源:https://blog.csdn.net/qusikao/article/details/135827066
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。