内容摘要
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
方法及其含义:
-
newFixedThreadPool(int nThreads)
:创建一个固定大小的线程池,该线程池中的线程数量始终保持不变,如果所有线程都在执行任务并且又有新任务提交,那么新任务会进入队列等待,直到有线程空闲出来,如果线程因为异常结束,那么会有新的线程来替代它。 -
newCachedThreadPool()
:创建一个可缓存的线程池,如果线程池的大小超过了处理需求,那么会回收空闲线程(60秒无任务的线程),当提交新任务时,如果没有空闲线程,那么会创建新的线程,该线程池的大小可以动态调整。 -
newSingleThreadExecutor()
:创建一个单线程的ExecutorService
,所有提交的任务都会按照这个顺序在一个线程中执行,如果该线程因为异常结束,那么会有新的线程来替代它。 -
newSingleThreadScheduledExecutor()
:创建一个单线程的ScheduledExecutorService
,该线程池可以调度在给定的延迟后执行命令,或者定期执行命令。 -
newScheduledThreadPool(int corePoolSize)
:创建一个可以调度在给定的延迟后执行命令,或者定期执行命令的线程池,该线程池的核心线程数量是固定的,但非核心线程数量可以动态调整(基于工作队列的大小和任务提交的速度)。
注意:尽管 Executors
提供了方便的工厂方法来创建线程池,但在生产环境中,建议使用 ThreadPoolExecutor
的直接构造方法来创建线程池,这样可以更细粒度地控制线程池的参数和配置。
另外,从Java 9开始,Executors
类中增加了 newWorkStealingPool
方法,用于创建一个工作窃取线程池,这在处理大量并行任务时非常有用,特别是当任务的执行时间不确定时。工作窃取算法可以帮助平衡各个线程之间的工作量,从而提高整体的吞吐量。
核心总结
Executors
是Java中创建线程池的便捷工具,其优点在于提供了多种类型的线程池以满足不同场景的需求,如固定大小、缓存、单线程等,这些线程池减少了资源管理的复杂性,并提供了性能优势,然而,Executors
的缺点在于其默认配置可能不适合所有场景,如工作队列大小和拒绝策略等,在复杂场景中,优先考虑使用ThreadPoolExecutor
以获得更多的线程控制权。
END!