在 Java 中,Executors
类提供了多种静态工厂方法来创建不同类型的线程池。学习线程池时,Executors
类不可或缺。掌握其用法、原理和适用场景,有助于在实际项目开发中顺利应用。以下是一些常用方法,我将逐一解释:
newCachedThreadPool()
:创建一个可缓存线程池。如果一个线程 60 秒未被使用,将被终止并从缓存中移除。newFixedThreadPool(int nThreads)
:创建一个固定大小的线程池,其中nThreads
指定池中的线程数量。newSingleThreadExecutor()
:创建一个单线程执行器,创建单个工作线程来执行任务。newScheduledThreadPool(int corePoolSize)
:创建一个固定大小的线程池,可根据需要创建新线程,但会按照给定的初始延迟执行任务。newWorkStealingPool(int parallelism)
:创建一个工作窃取线程池,使用多个队列,每个线程从自己的队列中窃取任务。newSingleThreadScheduledExecutor()
:创建一个单线程调度执行器,可根据需要创建新线程来执行任务。privilegedThreadFactory()
:创建一个线程工厂,用于创建具有特权访问权限的线程。defaultThreadFactory()
:创建一个默认线程工厂,用于创建没有特殊权限的标准线程。unconfigurableExecutorService(ExecutorService executor)
:将给定的ExecutorService
转换为不可配置版本,使调用者无法修改其配置。
这些方法提供了灵活的方式来创建和管理线程池,以满足不同的并发需求。下面,我将详细介绍这 9 种方法的实现和使用场景。
newCachedThreadPool()
newCachedThreadPool
方法是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个可缓存线程池,可根据需要动态创建新线程,并终止在一定时间内未使用的空闲线程。
实现原理
- 线程创建:当向线程池提交任务时,如果当前线程数小于核心池大小,将创建新线程。
- 线程重用:如果当前线程数等于核心池大小,新任务将被放入任务队列等待执行。
- 线程终止:空闲线程在指定时间(默认为 60 秒)内未被使用将被终止,以减少资源消耗。
源代码分析
在 Java 的java.util.concurrent
包中,Executors
类并不直接提供newCachedThreadPool
的实现。相反,它使用ThreadPoolExecutor
构造函数。以下是ThreadPoolExecutor
调用的示例:
java
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
参数解释
corePoolSize
:核心线程数,这里设置为 0,表示线程池不保留任何核心线程。maximumPoolSize
:最大线程数,设置为Integer.MAX_VALUE
,理论上允许无限数量的线程。keepAliveTime
:多余空闲线程在终止前等待新任务的最长时间,这里设置为 60 秒。unit
:keepAliveTime
参数的时间单位,这里是秒。workQueue
:任务队列。这里使用SynchronousQueue
,这是一个不存储元素的阻塞队列,要求每个插入操作都等待相应的移除操作。
实现过程
- 初始化 :调用
newCachedThreadPool
时,创建一个ThreadPoolExecutor
实例。 - 任务提交:向线程池提交任务时,池检查是否有空闲线程可用于执行任务。
- 线程创建 :如果没有空闲线程且当前线程数小于
maximumPoolSize
,则创建新线程来执行任务。 - 任务队列 :如果当前线程数已达到
maximumPoolSize
,任务将被放入SynchronousQueue
等待执行。 - 线程重用 :线程完成任务后,不会立即终止,而是尝试从
SynchronousQueue
获取新任务。 - 线程终止 :如果线程在
keepAliveTime
内未收到新任务,将终止。
这种设计使newCachedThreadPool
非常适合处理大量短期异步任务,因为它可以动态调整线程数量以适应不同的工作负载。然而,由于它可以创建无限数量的线程,因此必须考虑任务的特性和系统资源限制,以防止资源耗尽。
使用场景
适用于执行许多短期异步任务,特别是当任务的执行时间不确定时。例如,处理 Web 服务器上的大量并发请求或异步日志记录。
newFixedThreadPool(int nThreads)
newFixedThreadPool(int nThreads)
方法是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个具有固定线程数的线程池,确保池中的线程数量保持不变。
实现原理
- 固定线程数 :线程池中的线程数量始终保持为
nThreads
。 - 任务队列:提交到池中的任务首先由核心线程执行。如果所有核心线程都忙碌,新任务将被放入阻塞队列等待执行。
- 线程重用:池中的线程会被重用;完成一个任务后,线程将立即尝试从队列中获取下一个任务执行。
源代码分析
newFixedThreadPool
方法通过调用ThreadPoolExecutor
类的构造函数来实现。以下是ThreadPoolExecutor
调用的示例:
java
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads,
nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()
);
}
参数解释
corePoolSize
:核心线程数,设置为nThreads
,表示池始终有nThreads
个线程。maximumPoolSize
:最大线程数,也设置为nThreads
,确保池大小不超过nThreads
。keepAliveTime
:多余空闲线程在终止前等待新任务的最长时间,这里设置为 0,表示任何超过核心池大小的线程将立即终止。unit
:keepAliveTime
参数的时间单位,这里是毫秒。workQueue
:任务队列。这里使用LinkedBlockingQueue
,这是一个基于链表的阻塞队列,可以存储任意数量的任务。
实现过程
- 初始化 :调用
newFixedThreadPool
时,创建一个ThreadPoolExecutor
实例。 - 任务提交:向池提交任务时,检查是否有空闲核心线程可立即执行任务。
- 任务队列 :如果所有核心线程都忙碌,新任务将被放入
LinkedBlockingQueue
等待执行。 - 线程重用 :核心线程完成任务后,将尝试从
LinkedBlockingQueue
获取新任务继续执行。 - 线程数控制 :由于
keepAliveTime
设置为 0,任何超过核心池大小的线程将立即终止,确保池中的线程数量不超过nThreads
。
这种设计使newFixedThreadPool
非常适合处理大量稳定的任务流,因为它确保任务由固定数量的线程并行执行,避免线程不受控制地增长。然而,由于池大小是固定的,如果任务提交速率超过池的处理能力,任务可能会在队列中长时间等待。因此,使用newFixedThreadPool
时,根据任务的特性和预期工作负载设置nThreads
非常重要。
使用场景
适用于执行大量长时间运行的任务,且需要固定线程数量的情况。例如,同时运行多个数据加载或数据处理任务,同时限制并发以避免资源过载。
newSingleThreadExecutor()
newSingleThreadExecutor
方法是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个单线程执行器,确保所有任务按提交顺序依次执行,使用单个线程。
实现原理
- 单线程执行:线程池只包含一个线程,确保所有任务由这个单线程按顺序执行。
- 任务队列:如果在单线程忙碌时提交新任务,任务将被放入阻塞队列等待执行。
- 线程重用:单线程会被重用;完成一个任务后,它将立即尝试从队列中获取下一个任务执行。
源代码分析
newSingleThreadExecutor
方法通过调用ThreadPoolExecutor
类的构造函数来实现。以下是ThreadPoolExecutor
调用的示例:
java
public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(
1,
1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()
);
}
参数解释
corePoolSize
:核心线程数,设置为 1,表示池始终有一个核心线程。maximumPoolSize
:最大线程数,也设置为 1,确保池大小不超过一个线程。keepAliveTime
:多余空闲线程在终止前等待新任务的最长时间,这里设置为 0,表示线程空闲时将立即终止。unit
:keepAliveTime
参数的时间单位,这里是毫秒。workQueue
:任务队列。这里使用LinkedBlockingQueue
,这是一个可以存储任意数量任务的阻塞队列。
实现过程
- 初始化 :调用
newSingleThreadExecutor
时,创建一个ThreadPoolExecutor
实例。 - 任务提交 :向池提交任务时,如果核心线程空闲,它将立即执行任务;如果核心线程忙碌,任务将被放入
LinkedBlockingQueue
等待。 - 顺序执行:由于只有一个线程,所有任务按提交顺序执行。
- 任务队列 :如果核心线程正在执行任务,新任务将被放入
LinkedBlockingQueue
等待。 - 线程重用 :核心线程完成任务后,将尝试从
LinkedBlockingQueue
获取新任务继续执行。 - 线程数控制 :由于
keepAliveTime
设置为 0,核心线程在没有任务可执行时将立即终止。然而,因为corePoolSize
和maximumPoolSize
都为 1,线程池将立即重新创建一个新线程。
这种设计使newSingleThreadExecutor
非常适合需要保证任务顺序执行的场景,例如任务有依赖关系或必须按特定顺序执行的情况。此外,由于只有一个线程,它避免了多线程环境中固有的并发问题。然而,单线程执行也限制了并行处理能力;如果一个任务执行时间较长,后续任务可能会经历显著的延迟。因此,使用newSingleThreadExecutor
时,考虑任务的性质和顺序执行的要求非常重要。
使用场景
- 保证顺序执行:适用于需要任务按特定顺序执行的场景,例如队列中消息或事件的顺序处理。
- 单后台线程执行定期任务:适用于需要单个后台线程持续处理定期任务的情况。
通过使用newSingleThreadExecutor
,开发人员可以确保任务按提交顺序执行,而无需管理多个线程的复杂性和潜在问题。
newScheduledThreadPool(int corePoolSize)
newScheduledThreadPool
方法是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法用于创建一个固定大小的线程池,支持执行定时和周期性任务。
实现原理
- 定时任务:线程池可以执行具有指定延迟或固定间隔的任务。
- 固定线程数 :池中的线程数量限制为
corePoolSize
指定的大小。 - 任务队列:任务首先由核心线程执行。如果所有核心线程都忙碌,新任务将被放入延迟队列等待执行。
源代码分析
newScheduledThreadPool
方法通过调用ScheduledThreadPoolExecutor
类的构造函数来实现。以下是调用示例:
java
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor
是ThreadPoolExecutor
的子类,专门用于执行定时任务。ScheduledThreadPoolExecutor
构造函数的corePoolSize
参数定义了池中核心线程的数量。
内部,ScheduledThreadPoolExecutor
使用DelayedWorkQueue
作为其任务队列,该队列根据任务的预定执行时间对任务进行排序。
实现过程
- 初始化 :调用
newScheduledThreadPool
时,创建一个ScheduledThreadPoolExecutor
实例。 - 任务提交 :向池提交任务时,根据其预定执行时间将其放入
DelayedWorkQueue
。 - 任务调度 :池中的线程从
DelayedWorkQueue
中获取任务,并在预定时间到达时执行它们。 - 线程重用 :完成任务的线程将尝试从
DelayedWorkQueue
获取下一个任务。 - 线程数控制 :如果任务数量超过核心线程的处理能力,
ScheduledThreadPoolExecutor
将创建新线程来帮助处理任务,最多达到corePoolSize
定义的限制。
特性
- 线程工厂 :
ScheduledThreadPoolExecutor
允许设置线程工厂来创建具有特定属性的线程。 - 拒绝执行处理程序 :它允许设置
RejectedExecutionHandler
来处理无法接受的任务(例如,当池关闭或任务队列已满时)。 - 关闭行为 :与
ThreadPoolExecutor
不同,ScheduledThreadPoolExecutor
的shutdown
和shutdownNow
方法不会等待延迟任务完成。
newScheduledThreadPool
方法非常适合需要执行定时任务的场景,例如周期性后台任务或计划在特定时间运行的任务。然而,由于它基于固定大小的线程池,在高负载下,任务可能会排队等待执行。因此,在设计时考虑适当的corePoolSize
以满足性能要求非常重要。
使用场景
- 周期性任务执行:适用于需要定期执行任务的场景,例如定期数据备份或周期性状态检查。
- 延迟任务执行:适用于需要在未来某个时间点执行的任务,例如发送提醒或计划更新。
通过使用newScheduledThreadPool
,开发人员可以有效地管理和调度需要在固定间隔或特定时间执行的任务,确保任务由固定数量的线程正确处理。
newWorkStealingPool(int parallelism)
newWorkStealingPool(int parallelism)
方法是 Java 8 中引入的java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个工作窃取线程池,可提高并行任务的执行效率,特别是在多处理器系统上。
实现原理
- 工作窃取:在工作窃取线程池中,每个线程都有自己的任务队列。当一个线程完成其任务时,它会尝试从其他线程的队列中"窃取"任务。
- 并行级别 :线程池的大小由
parallelism
参数确定,通常等于主机上的处理器核心数量。 - 动态调整:工作窃取线程池可以动态添加或删除线程,以适应任务负载和线程利用率。
源代码分析
newWorkStealingPool
方法通过调用ForkJoinPool
类的静态工厂方法来实现。以下是调用示例:
java
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool(
parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null,
false
);
}
参数解释
parallelism
:并行级别,即线程池中的线程数量。ForkJoinPool.defaultForkJoinWorkerThreadFactory
:用于创建线程的默认线程工厂。null
:未捕获异常处理程序,这里未指定,因此如果任务抛出未捕获异常,它将传播到ForkJoinTask
的调用者。false
:表示这不是异步任务。
ForkJoinPool
内部使用ForkJoinWorkerThread
来执行任务,每个线程都有一个ForkJoinQueue
来存储任务。
实现过程
- 初始化 :调用
newWorkStealingPool
时,创建一个ForkJoinPool
实例。 - 任务提交:向池提交任务时,任务被放入调用线程的本地队列。
- 任务执行:每个线程首先尝试从其本地队列执行任务。
- 工作窃取:如果本地队列为空,线程尝试从其他线程的队列中窃取任务。
- 动态调整:线程池可以根据需要动态添加或删除线程。
特性
- 工作窃取:这种机制特别适合工作负载不均匀分布的情况,因为它减少了空闲时间并提高了资源利用率。
- 并行计算:适合可以分解为多个子任务的并行计算任务,因为任务可以被分割并将子任务提交到线程池。
- 减少竞争:由于每个线程都有自己的队列,锁竞争减少,提高了并发性能。
newWorkStealingPool
非常适合需要高并发和高吞吐量的场景,特别是在多处理器系统上。然而,由于工作窃取机制,它可能不适合任务执行时间非常短或任务数量非常少的场景,因为窃取本身可能会引入额外的开销。
使用场景
- 不均匀工作负载:适用于工作负载不均匀或可以分解为多个较小任务的任务,如图像处理或数据分析。
- 多核处理器利用:有效利用多核处理器的所有核心。
通过使用newWorkStealingPool
,开发人员可以有效地管理和执行并行任务,充分利用多核处理器的能力并提高并发应用程序的性能。
newSingleThreadScheduledExecutor()
newSingleThreadScheduledExecutor
是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个单线程调度执行器,可调度命令在给定延迟后运行或定期执行。
实现原理
- 单线程执行:执行器确保所有任务在单个线程中按顺序执行,维护任务执行顺序。
- 定时任务:支持延迟和定期任务执行。
- 任务队列:所有任务首先被放入任务队列,然后由单个线程按顺序执行。
源代码分析
newSingleThreadScheduledExecutor
方法通过调用ScheduledThreadPoolExecutor
类的构造函数来实现。以下是调用示例:
java
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new ScheduledThreadPoolExecutor(1);
}
这里,ScheduledThreadPoolExecutor
是ExecutorService
的一个实现,专门用于执行定时任务。构造函数有一个参数,即核心池大小。将其设置为 1 表示这是一个单线程执行器。
ScheduledThreadPoolExecutor
内部使用DelayedWorkQueue
作为任务队列。此队列可以根据任务的预定执行时间对任务进行排序。
实现过程
- 初始化 :调用
newSingleThreadScheduledExecutor
时,创建一个核心池大小为 1 的ScheduledThreadPoolExecutor
实例。 - 任务提交 :向执行器提交任务时,它被包装成
ScheduledFutureTask
或RunnableScheduledFuture
并放入DelayedWorkQueue
。 - 任务调度 :单线程不断从
DelayedWorkQueue
获取任务,并在预定时间执行它们。如果执行时间已到,任务将被执行;如果未到,线程将等待直到执行时间到达。 - 顺序执行:由于只有一个线程,所有任务按提交顺序执行。
- 定期任务:对于需要定期执行的任务,执行器在每次执行后重新计算下一次执行时间,并将任务放回队列。
特性
- 顺序维护 :
newSingleThreadScheduledExecutor
非常适合需要维护任务顺序的场景,例如任务有依赖关系或特定顺序的情况。 - 并发简化:只有一个线程避免了并发问题,简化了任务同步和状态管理。
- 定时执行:它为任务提供了强大的调度功能,例如定期维护或后台任务。
使用场景
- 顺序任务执行:适用于任务需要按特定顺序执行或有依赖关系的场景。
- 定时后台任务:适用于需要定期执行的任务,例如定期系统检查或维护任务。
privilegedThreadFactory()
privilegedThreadFactory
是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个线程工厂,生成具有特权访问权限的线程。这些线程可以访问系统属性、加载系统库和访问文件系统。
实现原理
- 特权访问:此工厂创建的线程有权访问系统资源,如加载系统属性和库。
- 线程创建:线程工厂创建新的线程实例,这些实例继承创建它们的线程的上下文。
源代码分析
privilegedThreadFactory
方法的实现细节在标准 Java 库中未公开暴露。然而,我们可以通过检查其一般工作方式来理解其功能。以下是privilegedThreadFactory
方法可能的调用示例:
java
public static ThreadFactory privilegedThreadFactory() {
return new PrivilegedThreadFactory();
}
这里,PrivilegedThreadFactory
是Executors
类中的私有静态内部类,实现了ThreadFactory
接口。ThreadFactory
接口定义了newThread(Runnable r)
方法用于创建新线程。
实现过程
- 初始化 :调用
privilegedThreadFactory
方法时,返回一个新的PrivilegedThreadFactory
实例。 - 线程创建 :当使用此工厂创建线程时,它调用
newThread(Runnable r)
方法。 - 特权访问 :在
newThread(Runnable r)
方法实现中,使用AccessController.doPrivileged
方法确保新创建的线程具有特权访问权限。 - 上下文继承:新线程通常继承创建它的线程的上下文,包括类加载器和其他设置。
示例
虽然我们无法查看privilegedThreadFactory
的精确实现,但我们可以提供一个示例实现来演示如何创建特权线程:
java
public class PrivilegedThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Thread run() {
return new Thread(r);
}
});
}
}
在这个示例中,PrivilegedAction
是一个实现PrivilegedAction
接口的匿名类,其中run
方法创建一个新线程。AccessController.doPrivileged
方法执行一个特权操作,以确保线程创建过程具有必要的权限。
特性
- 特权访问 :使用
privilegedThreadFactory
创建的线程可以访问敏感系统资源,适用于需要此类访问的应用程序。 - 简化安全 :使用
AccessController.doPrivileged
确保线程在执行期间被授予适当的安全权限。 - 上下文继承:新线程继承其父线程的上下文,包括类加载器和其他安全设置。
使用场景
- 访问系统资源:适用于需要线程具有更高权限以访问系统属性或执行文件 I/O 操作的应用程序。
- 安全敏感操作:确保执行安全敏感操作的线程具有必要的权限,以避免安全异常。
defaultThreadFactory()
defaultThreadFactory
是 Java java.util.concurrent
包中Executors
类的静态工厂方法。此方法创建一个默认线程工厂,生成具有标准属性的线程,没有特殊权限。
实现原理
- 标准线程创建:线程工厂生成具有默认属性的线程。
- 线程命名:创建的线程具有默认名称前缀,通常形式为"pool-x-thread-y",其中 x 和 y 是数字。
- 线程优先级 :线程优先级设置为
Thread.NORM_PRIORITY
,这是 Java 线程的默认优先级。 - 非守护线程:创建的线程是非守护线程,这意味着它们的存在将阻止 JVM 退出。
源代码分析
defaultThreadFactory
方法的详细实现未完全暴露,但我们可以从ThreadFactory
接口和一些可用的源代码片段推断其行为。
以下是defaultThreadFactory
方法的典型调用:
java
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
DefaultThreadFactory
是Executors
类中的私有静态内部类,实现了ThreadFactory
接口。ThreadFactory
接口定义了newThread(Runnable r)
方法用于创建新线程。
实现过程
- 初始化 :调用
defaultThreadFactory
方法时,返回一个新的DefaultThreadFactory
实例。 - 线程创建 :当使用此工厂创建线程时,它调用
newThread(Runnable r)
方法。 - 线程命名 :
newThread(Runnable r)
方法创建一个新的Thread
对象并设置默认线程名称。 - 线程组分配:新线程被分配到一个默认线程组。
- 线程优先级和守护状态:线程优先级设置为默认值,如果初始为守护线程,则将其设置为非守护线程。
示例
虽然无法看到defaultThreadFactory
的精确实现,但我们可以提供一个示例实现来演示如何创建具有默认属性的线程:
java
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s!= null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority()!= Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
在这个示例中,DefaultThreadFactory
使用AtomicInteger
确保池和线程编号的唯一性。创建的线程名称具有前缀"pool-x-thread-y",其中 x 和 y 是递增的数字。线程是非守护的,其优先级设置为Thread.NORM_PRIORITY
。
特性
- 标准线程属性 :使用
defaultThreadFactory
创建的线程具有标准的 Java 线程属性。 - 非特殊用途:此线程工厂适用于不需要特殊权限的应用程序。
- 非守护线程:这些线程的存在将阻止 JVM 退出,直到所有非守护线程完成执行。
使用场景
- 标准应用程序:适用于大多数需要具有默认属性线程的标准应用程序。
- ExecutorService 实现 :在不需要特殊线程属性时,通常用作
ExecutorService
实现的默认选择。
unconfigurableExecutorService(ExecutorService executor)
unconfigurableExecutorService
方法在 Java java.util.concurrent
包的Executors
类中,用于创建一个围绕ExecutorService
的不可配置包装器。这意味着一旦包装的ExecutorService
被创建,其配置就不能被更改,例如修改池大小或任务队列。
实现原理
- 封装 :将现有的
ExecutorService
包装在一个不可配置的代理中。 - 不可变配置 :任何更改配置的方法调用,如
shutdown
、shutdownNow
、setCorePoolSize
等,都将抛出UnsupportedOperationException
。 - 委托 :其他方法调用被委托给原始的
ExecutorService
。
源代码分析
unconfigurableExecutorService
方法的详细实现未完全暴露,因为它是Executors
类的私有方法的一部分。然而,我们可以根据ExecutorService
接口和代理机制推断其行为。
以下是unconfigurableExecutorService
方法的典型调用:
java
public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
return new FinalizableDelegatedExecutorService(executor);
}
FinalizableDelegatedExecutorService
是Executors
类中的私有静态内部类,实现了ExecutorService
接口,并将方法调用委托给另一个ExecutorService
。
实现过程
- 初始化 :调用
unconfigurableExecutorService
方法时,返回一个新的FinalizableDelegatedExecutorService
实例,该实例将原始的ExecutorService
作为参数。 - 方法拦截 :对
FinalizableDelegatedExecutorService
方法的调用首先被拦截。 - 配置修改拦截 :如果方法是用于修改配置的,如
shutdown
或shutdownNow
,则抛出UnsupportedOperationException
。 - 委托其他调用 :不涉及配置修改的调用,如
submit
或execute
,被委托给原始的ExecutorService
。
示例
以下是一个示例实现,演示如何创建一个不可配置的ExecutorService
代理:
java
public class UnconfigurableExecutorService implements ExecutorService {
private final ExecutorService executor;
public UnconfigurableExecutorService(ExecutorService executor) {
this.executor = executor;
}
@Override
public void shutdown() {
throw new UnsupportedOperationException("Shutdown not allowed");
}
@Override
public List<Runnable> shutdownNow() {
throw new UnsupportedOperationException("Shutdown not allowed");
}
@Override
public boolean isShutdown() {
return executor.isShutdown();
}
@Override
public boolean isTerminated() {
return executor.isTerminated();
}
@Override
public void execute(Runnable command) {
executor.execute(command);
}
}
在这个示例中,UnconfigurableExecutorService
拦截shutdown
和shutdownNow
方法并抛出UnsupportedOperationException
。其他方法直接委托给原始的ExecutorService
。
特性
- 不可变配置 :创建的
ExecutorService
包装器确保线程池的配置不能被外部更改。 - 共享环境中的安全性:有助于防止意外更改线程池的状态,提高多线程环境中的安全性。
- 维护原始行为 :其他方法保留原始
ExecutorService
的行为。
使用场景
- 共享线程池:适用于多个组件共享同一个线程池的情况,防止一个组件意外修改配置。
- 受控环境:在需要线程池配置保持一致且不可更改的环境中很有用。