线程管理神器:Executors全面解析

时间:2024-01-24 20:03:29

线程管理神器:Executors全面解析- 程序员古德

内容摘要

Executors在Java中提供了快速创建线程池的能力,其优点显著:它简化了线程管理,减少了代码量;提供了多种类型的线程池以适应不同场景;通过复用线程,降低了资源消耗,提高了系统响应速度和吞吐量。使用Executors,开发者能够更专注于业务逻辑,而无需深入底层线程细节。

官方文档:https://docx.iamqiang.com/jdk11/api/java.base/java/util/concurrent/Executors.html

核心概念

在Java中,Executors 是一个提供线程池功能的实用工具类,它允许更便捷地创建、管理和控制线程,从而优化资源使用和提高系统性能。

模拟一个例子,假如有一个公司的电商平台即将开始双十一大促互动,大促活动预计将带来前所未有的流量和订单量,在这种情况下,为了确保系统的稳定性和高效性,不能简单地为每个用户请求都创建一个新线程来处理,因为这样做会消耗大量系统资源,并且可能导致服务器崩溃,这是可以使用Executors

可以将Executors想象成一家高效的物流公司,当电商平台接收到用户订单时(就像物流公司接收到货物一样),不是直接为每个订单派一辆独立的货车去送货(这相当于为每个请求创建一个新线程),而是将这些订单放入一个集中的处理中心(线程池)。

这个处理中心(线程池)由Executors管理,它会根据当前的订单量(任务量)和系统资源情况,智能地分配有限的货车(线程)去送货(执行任务),如果订单量激增,Executors会自动排队并管理这些订单,确保每个订单都能得到及时处理,同时避免资源的浪费和系统的崩溃。

使用Executors可以更高效地管理线程,特别是在处理大量并发任务时,它能够显著提升系统的性能和稳定性。

代码案例

如下是一个Executors的使用案例,展示了用一个固定大小的线程池来并发执行任务,如下代码:

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.TimeUnit;  
  
public class ThreadPoolExample {  
  
    public static void main(String[] args) {  
        // 创建一个固定大小的线程池,其中包含了3个线程  
        ExecutorService executorService = Executors.newFixedThreadPool(3);  
  
        // 提交10个任务到线程池  
        for (int i = 0; i < 10; i++) {  
            final int taskId = i;  
            executorService.submit(() -> {  
                // 模拟任务执行,这里简单地打印任务ID和执行的线程名  
                System.out.println("Executing task " + taskId + " via " + Thread.currentThread().getName());  
                try {  
                    // 让线程休眠一段时间,模拟任务耗时  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    // 如果线程被中断,则处理中断  
                    e.printStackTrace();  
                }  
            });  
        }  
  
        // 关闭线程池(这会让线程池不再接受新的任务,但会继续处理队列中的任务)  
        executorService.shutdown();  
  
        // 等待所有任务完成(这里等待的时间比所有任务的总执行时间要长,以确保所有任务都能完成)  
        try {  
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {  
                // 如果等待超时后线程池仍未关闭,可以选择执行一些额外的操作,比如取消剩余任务  
                System.err.println("Not all tasks finished in the given time.");  
            }  
        } catch (InterruptedException ie) {  
            // 如果当前线程在等待过程中被中断,则处理中断  
            System.err.println("Interrupted while waiting for tasks to complete.");  
            // 重新设置中断状态,以便调用者能够知道发生了中断  
            Thread.currentThread().interrupt();  
        }  
  
        System.out.println("All tasks are finished.");  
    }  
}

在上面代码中,通过Executors.newFixedThreadPool(3)创建了一个包含3个线程的线程池,然后提交了10个任务到线程池,每个任务都简单地打印出任务ID和执行它的线程名,然后休眠1秒钟来模拟任务执行。

这里实例中的代码是非常简单的,并且只是为了演示线程池的基本用法,在实际应用中,可能会有更复杂和耗时的任务需要处理。

核心API说明

Executors 是 Java 并发库 java.util.concurrent 中的一个实用类,用于创建和管理各种线程池和 ExecutorService,该类提供了多个工厂方法来创建不同类型的 ExecutorService,以下是一些常用的 Executors 方法及其含义:

  1. newFixedThreadPool(int nThreads):创建一个固定大小的线程池,该线程池中的线程数量始终保持不变,如果所有线程都在执行任务并且又有新任务提交,那么新任务会进入队列等待,直到有线程空闲出来,如果线程因为异常结束,那么会有新的线程来替代它。
  2. newCachedThreadPool():创建一个可缓存的线程池,如果线程池的大小超过了处理需求,那么会回收空闲线程(60秒无任务的线程),当提交新任务时,如果没有空闲线程,那么会创建新的线程,该线程池的大小可以动态调整。
  3. newSingleThreadExecutor():创建一个单线程的 ExecutorService,所有提交的任务都会按照这个顺序在一个线程中执行,如果该线程因为异常结束,那么会有新的线程来替代它。
  4. newSingleThreadScheduledExecutor():创建一个单线程的 ScheduledExecutorService,该线程池可以调度在给定的延迟后执行命令,或者定期执行命令。
  5. newScheduledThreadPool(int corePoolSize):创建一个可以调度在给定的延迟后执行命令,或者定期执行命令的线程池,该线程池的核心线程数量是固定的,但非核心线程数量可以动态调整(基于工作队列的大小和任务提交的速度)。

注意:尽管 Executors 提供了方便的工厂方法来创建线程池,但在生产环境中,建议使用 ThreadPoolExecutor 的直接构造方法来创建线程池,这样可以更细粒度地控制线程池的参数和配置。

另外,从Java 9开始,Executors 类中增加了 newWorkStealingPool 方法,用于创建一个工作窃取线程池,这在处理大量并行任务时非常有用,特别是当任务的执行时间不确定时。工作窃取算法可以帮助平衡各个线程之间的工作量,从而提高整体的吞吐量。

核心总结

线程管理神器:Executors全面解析 - 程序员古德

Executors是Java中创建线程池的便捷工具,其优点在于提供了多种类型的线程池以满足不同场景的需求,如固定大小、缓存、单线程等,这些线程池减少了资源管理的复杂性,并提供了性能优势,然而,Executors的缺点在于其默认配置可能不适合所有场景,如工作队列大小和拒绝策略等,在复杂场景中,优先考虑使用ThreadPoolExecutor以获得更多的线程控制权。

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

END!