java并发:线程池之Executor、ExecutorService
一、序言
当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创建线程会降低系统的效率,因为创建和销毁线程均需要一定的时间。
线程池可以使线程得到复用,所谓线程复用就是线程在执行完一个任务后并不被销毁,该线程可以继续执行其他的任务。
java.lang.concurrent包中的Executors类为我们创建线程池提供了方便。
二、Executors的简单示例
此处我们先来看一个简单的例子,如下:
package com.soft; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorsDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // ExecutorService executor = Executors.newSingleThreadExecutor(); // ExecutorService executor = Executors.newCachedThreadPool(); ExecutorService executor = Executors.newFixedThreadPool(5); Thread.sleep(5*1000);//方便监控工具能捕获到 for (int i = 0; i < 10; i++) { final int no = i; Runnable runnable = new Runnable() { public void run() { try { System.out.println("into" + no); Thread.sleep(1000L); System.out.println("end" + no); } catch (InterruptedException e) { e.printStackTrace(); } } }; executor.execute(runnable); }//End for executor.shutdown(); System.out.println("Thread Main End!"); } }
其运行结果如下:
into0 into3 Thread Main End! into4 into1 into2 end0 into5 end3 end1 end4 into8 into6 into7 end2 into9 end5 end7 end8 end6 end9
解说:
ExecutorService有一个execute()方法,方法的参数是Runnable类型,通过该方法可将一个任务添加到线程池。
上面这个例子在任意某一时刻只有5个线程在执行,这是因为上述代码通过Executors.newFixedThreadPool(5)创建了一个固定长度的线程池(长度为5)。
三、Executors提供的线程池
Executors是线程的工厂类,也可以说是一个线程池工具类,它调用其内部静态方法(如newFixedThreadPool()等)即可创建一个线程池。
Executors提供不同的线程池机制,请参照下面这段摘录:
Executors提供的两个newFixedThreadPool方法如下:
/** * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue. At any point, at most * {@code nThreads} threads will be active processing tasks. * If additional tasks are submitted when all threads are active, * they will wait in the queue until a thread is available. * If any thread terminates due to a failure during execution * prior to shutdown, a new one will take its place if needed to * execute subsequent tasks. The threads in the pool will exist * until it is explicitly {@link ExecutorService#shutdown shutdown}. * * @param nThreads the number of threads in the pool * @return the newly created thread pool * @throws IllegalArgumentException if {@code nThreads <= 0} */ public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
/** * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue, using the provided * ThreadFactory to create new threads when needed. At any point, * at most {@code nThreads} threads will be active processing * tasks. If additional tasks are submitted when all threads are * active, they will wait in the queue until a thread is * available. If any thread terminates due to a failure during * execution prior to shutdown, a new one will take its place if * needed to execute subsequent tasks. The threads in the pool will * exist until it is explicitly {@link ExecutorService#shutdown * shutdown}. * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads * @return the newly created thread pool * @throws NullPointerException if threadFactory is null * @throws IllegalArgumentException if {@code nThreads <= 0} */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); }
四、Executor、ExecutorService
Executor、ExecutorService之间的关系如下图所示:
五、ExecutorService的生命周期
在本文最开始的那个示例中,有一句代码,如下:
executor.shutdown();
ExecutorService的实现类——ThreadPoolExecutor中的shutdown方法的实现如下:
/** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * * <p>This method does not wait for previously submitted tasks to * complete execution. Use {@link #awaitTermination awaitTermination} * to do that. * * @throws SecurityException {@inheritDoc} */ public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } tryTerminate(); }
该语句并不是终止线程的运行,而是禁止在这个executor中添加新的任务,下文描述了该语句对于ExecutorService的意义。
线程池的状态
- RUNNING
接受新任务并且处理阻塞队列里的任务。
- SHUTDOWN
拒绝新任务但是处理阻塞队列里的任务。
- STOP
拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务。
- TIDYING
所有任务都执行完(包含阻塞队列里面的任务)后,当前线程池活动线程数为 0,则调用 terminated 方法。
- TERMINATED
终止状态。
线程池状态转换
- RUNNING -> SHUTDOWN
显式调用 shutdown()方法或者隐式调用了 finalize()方法里面的 shutdown()方法。
- RUNNING 或 SHUTDOWN -> STOP
显式调用 shutdownNow() 方法。
- SHUTDOWN -> TIDYING
当线程池和任务队列都为空时。
- STOP->TIDYING
当线程池为空时。
- TIDING-> TERMINATED
当hook方法 terminated() 执行完成时。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?