用Java Executors创建线程池的9种方法

Executors线程池创建方式及线程核心参数配置 系列文章目录 文章目录系列文章目录前言一、Executors常用的线程池类型二、线程池中队列常用类型三、线程池原理、以及核心参数四、线程池的拒绝策略五、java实现创建线程池总结 前言 为什么要用线程池,因为“池”的思想就是以空间换时间,就像数据库连接池一样,好处就是:1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。2线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 提示:以下是本篇文章正文内容,下面案例可供参 阅读详情

在 Java 中,Executors类提供了多种静态工厂方法来创建不同类型的线程池。学习线程池时,Executors类不可或缺。掌握其用法、原理和适用场景,有助于在实际项目开发中顺利应用。以下是一些常用方法,我将逐一解释:

  1. newCachedThreadPool():创建一个可缓存线程池。如果一个线程 60 秒未被使用,将被终止并从缓存中移除。
  2. newFixedThreadPool(int nThreads):创建一个固定大小的线程池,其中nThreads指定池中的线程数量。
  3. newSingleThreadExecutor():创建一个单线程执行器,创建单个工作线程来执行任务。
  4. newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,可根据需要创建新线程,但会按照给定的初始延迟执行任务。
  5. newWorkStealingPool(int parallelism):创建一个工作窃取线程池,使用多个队列,每个线程从自己的队列中窃取任务。
  6. newSingleThreadScheduledExecutor():创建一个单线程调度执行器,可根据需要创建新线程来执行任务。
  7. privilegedThreadFactory():创建一个线程工厂,用于创建具有特权访问权限的线程。
  8. defaultThreadFactory():创建一个默认线程工厂,用于创建没有特殊权限的标准线程。
  9. unconfigurableExecutorService(ExecutorService executor):将给定的ExecutorService转换为不可配置版本,使调用者无法修改其配置。

这些方法提供了灵活的方式来创建和管理线程池,以满足不同的并发需求。下面,我将详细介绍这 9 种方法的实现和使用场景。

newCachedThreadPool()

newCachedThreadPool方法是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个可缓存线程池,可根据需要动态创建新线程,并终止在一定时间内未使用的空闲线程。

实现原理

  1. 线程创建:当向线程池提交任务时,如果当前线程数小于核心池大小,将创建新线程。
  2. 线程重用:如果当前线程数等于核心池大小,新任务将被放入任务队列等待执行。
  3. 线程终止:空闲线程在指定时间(默认为 60 秒)内未被使用将被终止,以减少资源消耗。

源代码分析

在 Java 的java.util.concurrent包中,Executors类并不直接提供newCachedThreadPool的实现。相反,它使用ThreadPoolExecutor构造函数。以下是ThreadPoolExecutor调用的示例:


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,这是一个不存储元素的阻塞队列,要求每个插入操作都等待相应的移除操作。

实现过程

  1. 初始化:调用newCachedThreadPool时,创建一个ThreadPoolExecutor实例。
  2. 任务提交:向线程池提交任务时,池检查是否有空闲线程可用于执行任务。
  3. 线程创建:如果没有空闲线程且当前线程数小于maximumPoolSize,则创建新线程来执行任务。
  4. 任务队列:如果当前线程数已达到maximumPoolSize,任务将被放入SynchronousQueue等待执行。
  5. 线程重用:线程完成任务后,不会立即终止,而是尝试从SynchronousQueue获取新任务。
  6. 线程终止:如果线程在keepAliveTime内未收到新任务,将终止。

这种设计使newCachedThreadPool非常适合处理大量短期异步任务,因为它可以动态调整线程数量以适应不同的工作负载。然而,由于它可以创建无限数量的线程,因此必须考虑任务的特性和系统资源限制,以防止资源耗尽。

使用场景

适用于执行许多短期异步任务,特别是当任务的执行时间不确定时。例如,处理 Web 服务器上的大量并发请求或异步日志记录。

newFixedThreadPool(int nThreads)

newFixedThreadPool(int nThreads)方法是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个具有固定线程数的线程池,确保池中的线程数量保持不变。

实现原理

  1. 固定线程数:线程池中的线程数量始终保持为nThreads。
  2. 任务队列:提交到池中的任务首先由核心线程执行。如果所有核心线程都忙碌,新任务将被放入阻塞队列等待执行。
  3. 线程重用:池中的线程会被重用;完成一个任务后,线程将立即尝试从队列中获取下一个任务执行。

源代码分析

newFixedThreadPool方法通过调用ThreadPoolExecutor类的构造函数来实现。以下是ThreadPoolExecutor调用的示例:


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,这是一个基于链表的阻塞队列,可以存储任意数量的任务。

实现过程

  1. 初始化:调用newFixedThreadPool时,创建一个ThreadPoolExecutor实例。
  2. 任务提交:向池提交任务时,检查是否有空闲核心线程可立即执行任务。
  3. 任务队列:如果所有核心线程都忙碌,新任务将被放入LinkedBlockingQueue等待执行。
  4. 线程重用:核心线程完成任务后,将尝试从LinkedBlockingQueue获取新任务继续执行。
  5. 线程数控制:由于keepAliveTime设置为 0,任何超过核心池大小的线程将立即终止,确保池中的线程数量不超过nThreads。

这种设计使newFixedThreadPool非常适合处理大量稳定的任务流,因为它确保任务由固定数量的线程并行执行,避免线程不受控制地增长。然而,由于池大小是固定的,如果任务提交速率超过池的处理能力,任务可能会在队列中长时间等待。因此,使用newFixedThreadPool时,根据任务的特性和预期工作负载设置nThreads非常重要。

使用场景

适用于执行大量长时间运行的任务,且需要固定线程数量的情况。例如,同时运行多个数据加载或数据处理任务,同时限制并发以避免资源过载。

newSingleThreadExecutor()

newSingleThreadExecutor方法是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个单线程执行器,确保所有任务按提交顺序依次执行,使用单个线程。

实现原理

  1. 单线程执行:线程池只包含一个线程,确保所有任务由这个单线程按顺序执行。
  2. 任务队列:如果在单线程忙碌时提交新任务,任务将被放入阻塞队列等待执行。
  3. 线程重用:单线程会被重用;完成一个任务后,它将立即尝试从队列中获取下一个任务执行。

源代码分析

newSingleThreadExecutor方法通过调用ThreadPoolExecutor类的构造函数来实现。以下是ThreadPoolExecutor调用的示例:


public static ExecutorService newSingleThreadExecutor() {
    return new ThreadPoolExecutor(
        1, 
        1, 
        0L, TimeUnit.MILLISECONDS, 
        new LinkedBlockingQueue() 
    );
}

参数解释

  • corePoolSize:核心线程数,设置为 1,表示池始终有一个核心线程。
  • maximumPoolSize:最大线程数,也设置为 1,确保池大小不超过一个线程。
  • keepAliveTime:多余空闲线程在终止前等待新任务的最长时间,这里设置为 0,表示线程空闲时将立即终止。
  • unit:keepAliveTime参数的时间单位,这里是毫秒。
  • workQueue:任务队列。这里使用LinkedBlockingQueue,这是一个可以存储任意数量任务的阻塞队列。

实现过程

  1. 初始化:调用newSingleThreadExecutor时,创建一个ThreadPoolExecutor实例。
  2. 任务提交:向池提交任务时,如果核心线程空闲,它将立即执行任务;如果核心线程忙碌,任务将被放入LinkedBlockingQueue等待。
  3. 顺序执行:由于只有一个线程,所有任务按提交顺序执行。
  4. 任务队列:如果核心线程正在执行任务,新任务将被放入LinkedBlockingQueue等待。
  5. 线程重用:核心线程完成任务后,将尝试从LinkedBlockingQueue获取新任务继续执行。
  6. 线程数控制:由于keepAliveTime设置为 0,核心线程在没有任务可执行时将立即终止。然而,因为corePoolSize和maximumPoolSize都为 1,线程池将立即重新创建一个新线程。

这种设计使newSingleThreadExecutor非常适合需要保证任务顺序执行的场景,例如任务有依赖关系或必须按特定顺序执行的情况。此外,由于只有一个线程,它避免了多线程环境中固有的并发问题。然而,单线程执行也限制了并行处理能力;如果一个任务执行时间较长,后续任务可能会经历显著的延迟。因此,使用newSingleThreadExecutor时,考虑任务的性质和顺序执行的要求非常重要。

使用场景

  1. 保证顺序执行:适用于需要任务按特定顺序执行的场景,例如队列中消息或事件的顺序处理。
  2. 单后台线程执行定期任务:适用于需要单个后台线程持续处理定期任务的情况。

通过使用newSingleThreadExecutor,开发人员可以确保任务按提交顺序执行,而无需管理多个线程的复杂性和潜在问题。

newScheduledThreadPool(int corePoolSize)

newScheduledThreadPool方法是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法用于创建一个固定大小的线程池,支持执行定时和周期性任务。

实现原理

  1. 定时任务:线程池可以执行具有指定延迟或固定间隔的任务。
  2. 固定线程数:池中的线程数量限制为corePoolSize指定的大小。
  3. 任务队列:任务首先由核心线程执行。如果所有核心线程都忙碌,新任务将被放入延迟队列等待执行。

源代码分析

newScheduledThreadPool方法通过调用ScheduledThreadPoolExecutor类的构造函数来实现。以下是调用示例:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,专门用于执行定时任务。ScheduledThreadPoolExecutor构造函数的corePoolSize参数定义了池中核心线程的数量。

内部,ScheduledThreadPoolExecutor使用DelayedWorkQueue作为其任务队列,该队列根据任务的预定执行时间对任务进行排序。

实现过程

  1. 初始化:调用newScheduledThreadPool时,创建一个ScheduledThreadPoolExecutor实例。
  2. 任务提交:向池提交任务时,根据其预定执行时间将其放入DelayedWorkQueue。
  3. 任务调度:池中的线程从DelayedWorkQueue中获取任务,并在预定时间到达时执行它们。
  4. 线程重用:完成任务的线程将尝试从DelayedWorkQueue获取下一个任务。
  5. 线程数控制:如果任务数量超过核心线程的处理能力,ScheduledThreadPoolExecutor将创建新线程来帮助处理任务,最多达到corePoolSize定义的限制。

特性

  1. 线程工厂:ScheduledThreadPoolExecutor允许设置线程工厂来创建具有特定属性的线程。
  2. 拒绝执行处理程序:它允许设置RejectedExecutionHandler来处理无法接受的任务(例如,当池关闭或任务队列已满时)。
  3. 关闭行为:与ThreadPoolExecutor不同,ScheduledThreadPoolExecutor的shutdown和shutdownNow方法不会等待延迟任务完成。

newScheduledThreadPool方法非常适合需要执行定时任务的场景,例如周期性后台任务或计划在特定时间运行的任务。然而,由于它基于固定大小的线程池,在高负载下,任务可能会排队等待执行。因此,在设计时考虑适当的corePoolSize以满足性能要求非常重要。

使用场景

  1. 周期性任务执行:适用于需要定期执行任务的场景,例如定期数据备份或周期性状态检查。
  2. 延迟任务执行:适用于需要在未来某个时间点执行的任务,例如发送提醒或计划更新。

通过使用newScheduledThreadPool,开发人员可以有效地管理和调度需要在固定间隔或特定时间执行的任务,确保任务由固定数量的线程正确处理。

newWorkStealingPool(int parallelism)

newWorkStealingPool(int parallelism)方法是 Java 8 中引入的java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个工作窃取线程池,可提高并行任务的执行效率,特别是在多处理器系统上。

实现原理

  1. 工作窃取:在工作窃取线程池中,每个线程都有自己的任务队列。当一个线程完成其任务时,它会尝试从其他线程的队列中“窃取”任务。
  2. 并行级别:线程池的大小由parallelism参数确定,通常等于主机上的处理器核心数量。
  3. 动态调整:工作窃取线程池可以动态添加或删除线程,以适应任务负载和线程利用率。

源代码分析

newWorkStealingPool方法通过调用ForkJoinPool类的静态工厂方法来实现。以下是调用示例:

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool(
        parallelism,
        ForkJoinPool.defaultForkJoinWorkerThreadFactory,
        null, 
        false 
    );
}

参数解释

  • parallelism:并行级别,即线程池中的线程数量。
  • ForkJoinPool.defaultForkJoinWorkerThreadFactory:用于创建线程的默认线程工厂。
  • null:未捕获异常处理程序,这里未指定,因此如果任务抛出未捕获异常,它将传播到ForkJoinTask的调用者。
  • false:表示这不是异步任务。

ForkJoinPool内部使用ForkJoinWorkerThread来执行任务,每个线程都有一个ForkJoinQueue来存储任务。

实现过程

  1. 初始化:调用newWorkStealingPool时,创建一个ForkJoinPool实例。
  2. 任务提交:向池提交任务时,任务被放入调用线程的本地队列。
  3. 任务执行:每个线程首先尝试从其本地队列执行任务。
  4. 工作窃取:如果本地队列为空,线程尝试从其他线程的队列中窃取任务。
  5. 动态调整:线程池可以根据需要动态添加或删除线程。

特性

  1. 工作窃取:这种机制特别适合工作负载不均匀分布的情况,因为它减少了空闲时间并提高了资源利用率。
  2. 并行计算:适合可以分解为多个子任务的并行计算任务,因为任务可以被分割并将子任务提交到线程池。
  3. 减少竞争:由于每个线程都有自己的队列,锁竞争减少,提高了并发性能。

newWorkStealingPool非常适合需要高并发和高吞吐量的场景,特别是在多处理器系统上。然而,由于工作窃取机制,它可能不适合任务执行时间非常短或任务数量非常少的场景,因为窃取本身可能会引入额外的开销。

使用场景

  1. 不均匀工作负载:适用于工作负载不均匀或可以分解为多个较小任务的任务,如图像处理或数据分析。
  2. 多核处理器利用:有效利用多核处理器的所有核心。

通过使用newWorkStealingPool,开发人员可以有效地管理和执行并行任务,充分利用多核处理器的能力并提高并发应用程序的性能。

newSingleThreadScheduledExecutor()

newSingleThreadScheduledExecutor是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个单线程调度执行器,可调度命令在给定延迟后运行或定期执行。

实现原理

  1. 单线程执行:执行器确保所有任务在单个线程中按顺序执行,维护任务执行顺序。
  2. 定时任务:支持延迟和定期任务执行。
  3. 任务队列:所有任务首先被放入任务队列,然后由单个线程按顺序执行。

源代码分析

newSingleThreadScheduledExecutor方法通过调用ScheduledThreadPoolExecutor类的构造函数来实现。以下是调用示例:

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new ScheduledThreadPoolExecutor(1);
}

这里,ScheduledThreadPoolExecutor是ExecutorService的一个实现,专门用于执行定时任务。构造函数有一个参数,即核心池大小。将其设置为 1 表示这是一个单线程执行器。

ScheduledThreadPoolExecutor内部使用DelayedWorkQueue作为任务队列。此队列可以根据任务的预定执行时间对任务进行排序。

实现过程

  1. 初始化:调用newSingleThreadScheduledExecutor时,创建一个核心池大小为 1 的ScheduledThreadPoolExecutor实例。
  2. 任务提交:向执行器提交任务时,它被包装成ScheduledFutureTask或RunnableScheduledFuture并放入DelayedWorkQueue。
  3. 任务调度:单线程不断从DelayedWorkQueue获取任务,并在预定时间执行它们。如果执行时间已到,任务将被执行;如果未到,线程将等待直到执行时间到达。
  4. 顺序执行:由于只有一个线程,所有任务按提交顺序执行。
  5. 定期任务:对于需要定期执行的任务,执行器在每次执行后重新计算下一次执行时间,并将任务放回队列。

特性

  1. 顺序维护:newSingleThreadScheduledExecutor非常适合需要维护任务顺序的场景,例如任务有依赖关系或特定顺序的情况。
  2. 并发简化:只有一个线程避免了并发问题,简化了任务同步和状态管理。
  3. 定时执行:它为任务提供了强大的调度功能,例如定期维护或后台任务。

使用场景

  1. 顺序任务执行:适用于任务需要按特定顺序执行或有依赖关系的场景。
  2. 定时后台任务:适用于需要定期执行的任务,例如定期系统检查或维护任务。

privilegedThreadFactory()

privilegedThreadFactory是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个线程工厂,生成具有特权访问权限的线程。这些线程可以访问系统属性、加载系统库和访问文件系统。

实现原理

  1. 特权访问:此工厂创建的线程有权访问系统资源,如加载系统属性和库。
  2. 线程创建:线程工厂创建新的线程实例,这些实例继承创建它们的线程的上下文。

源代码分析

privilegedThreadFactory方法的实现细节在标准 Java 库中未公开暴露。然而,我们可以通过检查其一般工作方式来理解其功能。以下是privilegedThreadFactory方法可能的调用示例:


public static ThreadFactory privilegedThreadFactory() {
    return new PrivilegedThreadFactory();
}

这里,PrivilegedThreadFactory是Executors类中的私有静态内部类,实现了ThreadFactory接口。ThreadFactory接口定义了newThread(Runnable r)方法用于创建新线程。

实现过程

  1. 初始化:调用privilegedThreadFactory方法时,返回一个新的PrivilegedThreadFactory实例。
  2. 线程创建:当使用此工厂创建线程时,它调用newThread(Runnable r)方法。
  3. 特权访问:在newThread(Runnable r)方法实现中,使用AccessController.doPrivileged方法确保新创建的线程具有特权访问权限。
  4. 上下文继承:新线程通常继承创建它的线程的上下文,包括类加载器和其他设置。

示例

虽然我们无法查看privilegedThreadFactory的精确实现,但我们可以提供一个示例实现来演示如何创建特权线程:

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方法执行一个特权操作,以确保线程创建过程具有必要的权限。

特性

  1. 特权访问:使用privilegedThreadFactory创建的线程可以访问敏感系统资源,适用于需要此类访问的应用程序。
  2. 简化安全:使用AccessController.doPrivileged确保线程在执行期间被授予适当的安全权限。
  3. 上下文继承:新线程继承其父线程的上下文,包括类加载器和其他安全设置。

使用场景

  1. 访问系统资源:适用于需要线程具有更高权限以访问系统属性或执行文件 I/O 操作的应用程序。
  2. 安全敏感操作:确保执行安全敏感操作的线程具有必要的权限,以避免安全异常。

defaultThreadFactory()

defaultThreadFactory是 Java java.util.concurrent包中Executors类的静态工厂方法。此方法创建一个默认线程工厂,生成具有标准属性的线程,没有特殊权限。

实现原理

  1. 标准线程创建:线程工厂生成具有默认属性的线程。
  2. 线程命名:创建的线程具有默认名称前缀,通常形式为“pool-x-thread-y”,其中 x 和 y 是数字。
  3. 线程优先级:线程优先级设置为Thread.NORM_PRIORITY,这是 Java 线程的默认优先级。
  4. 非守护线程:创建的线程是非守护线程,这意味着它们的存在将阻止 JVM 退出。

源代码分析

defaultThreadFactory方法的详细实现未完全暴露,但我们可以从ThreadFactory接口和一些可用的源代码片段推断其行为。

以下是defaultThreadFactory方法的典型调用:


public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}

DefaultThreadFactory是Executors类中的私有静态内部类,实现了ThreadFactory接口。ThreadFactory接口定义了newThread(Runnable r)方法用于创建新线程。

实现过程

  1. 初始化:调用defaultThreadFactory方法时,返回一个新的DefaultThreadFactory实例。
  2. 线程创建:当使用此工厂创建线程时,它调用newThread(Runnable r)方法。
  3. 线程命名:newThread(Runnable r)方法创建一个新的Thread对象并设置默认线程名称。
  4. 线程组分配:新线程被分配到一个默认线程组。
  5. 线程优先级和守护状态:线程优先级设置为默认值,如果初始为守护线程,则将其设置为非守护线程。

示例

虽然无法看到defaultThreadFactory的精确实现,但我们可以提供一个示例实现来演示如何创建具有默认属性的线程:

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。

特性

  1. 标准线程属性:使用defaultThreadFactory创建的线程具有标准的 Java 线程属性。
  2. 非特殊用途:此线程工厂适用于不需要特殊权限的应用程序。
  3. 非守护线程:这些线程的存在将阻止 JVM 退出,直到所有非守护线程完成执行。

使用场景

  1. 标准应用程序:适用于大多数需要具有默认属性线程的标准应用程序。
  2. ExecutorService 实现:在不需要特殊线程属性时,通常用作ExecutorService实现的默认选择。

unconfigurableExecutorService(ExecutorService executor)

unconfigurableExecutorService方法在 Java java.util.concurrent包的Executors类中,用于创建一个围绕ExecutorService的不可配置包装器。这意味着一旦包装的ExecutorService被创建,其配置就不能被更改,例如修改池大小或任务队列。

实现原理

  1. 封装:将现有的ExecutorService包装在一个不可配置的代理中。
  2. 不可变配置:任何更改配置的方法调用,如shutdown、shutdownNow、setCorePoolSize等,都将抛出UnsupportedOperationException。
  3. 委托:其他方法调用被委托给原始的ExecutorService。

源代码分析

unconfigurableExecutorService方法的详细实现未完全暴露,因为它是Executors类的私有方法的一部分。然而,我们可以根据ExecutorService接口和代理机制推断其行为。

以下是unconfigurableExecutorService方法的典型调用:

public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
    return new FinalizableDelegatedExecutorService(executor);
}

FinalizableDelegatedExecutorService是Executors类中的私有静态内部类,实现了ExecutorService接口,并将方法调用委托给另一个ExecutorService。

实现过程

  1. 初始化:调用unconfigurableExecutorService方法时,返回一个新的FinalizableDelegatedExecutorService实例,该实例将原始的ExecutorService作为参数。
  2. 方法拦截:对FinalizableDelegatedExecutorService方法的调用首先被拦截。
  3. 配置修改拦截:如果方法是用于修改配置的,如shutdown或shutdownNow,则抛出UnsupportedOperationException。
  4. 委托其他调用:不涉及配置修改的调用,如submit或execute,被委托给原始的ExecutorService。

示例

以下是一个示例实现,演示如何创建一个不可配置的ExecutorService代理:

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。

特性

  1. 不可变配置:创建的ExecutorService包装器确保线程池的配置不能被外部更改。
  2. 共享环境中的安全性:有助于防止意外更改线程池的状态,提高多线程环境中的安全性。
  3. 维护原始行为:其他方法保留原始ExecutorService的行为。

使用场景

  1. 共享线程池:适用于多个组件共享同一个线程池的情况,防止一个组件意外修改配置。
  2. 受控环境:在需要线程池配置保持一致且不可更改的环境中很有用。

线程池——Executor、Executors、ExecutorService、ThreadPoolExecutor、ThreadPoolTaskExecutor之间的区别 java.util.concurrent.Executor 负责线程的使用和调度的根接口|–ExecutorService 子接口: 线程池的主要接口|–ThreadPoolExecutor 线程池的实现类|–ScheduledExceutorService 子接口: 负责线程的调度|–ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor,实现了ScheduledExecutorService。 阅读详情

相关推荐

聊聊 ThreadPoolExecutor 线程池及其源码

在我们的实际开发中,通常使用线程池创建线程资源。而线程池本质上是一种池化技术,利用资源复用的思想,复用线程资源。

Huangjiazhen711的博客 561

【Executors】线程池的4种常见创建方式

这世界上没有优秀的理念,只有脚踏实地的结果 。 一、 创建缓存型线程池(CachedThreadPool) 1、定义: Executors.newCacheThreadPool():可缓存线程池,先查看线程池中有没有之前创建的线程,如果有则直接使用。否则就新创建一个新的线程加入线程池中,我们经常用此类线程池来执行一些业务处理时间很短的任务。 2、示例代码: 情况1: public static void main(String[] args) { //......

我家有个小喵咪 1万+

Java并发编程(10)—— Java Executors类的9种创建线程池的方法及应用场景分析

然而,由于线程池的大小是固定的,如果任务提交的速率超过了线程池的处理能力,可能会导致任务在队列中等待较长时间。privilegedThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个线程工厂,该工厂能够产生具有特权访问的线程。newSingleThreadExecutor 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个单线程的执行器。

shangjg3的博客 457

Java Executors类的9种创建线程池的方法及应用场景分析

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。在学习线程池的过程中,一定避不开Executors类,掌握这个类的使用、原理、使用场景,对于实际项目开发时,运用自如,以下是一些常用的方法,V哥来一一细说:: 创建一个可缓存的线程池,如果线程池中的线程超过60秒没有被使用,它们将被终止并从缓存中移除。: 创建一个固定大小的线程池,其中 nThreads 指定了线程池中线程的数量。: 创建一个单线程的执行器,它创建单个工作线程来执行任务。: 创建一个固定大小的线程池,它可以根据

威哥爱编程,华为 HDE,鸿蒙极客、优快云博客专家、《鸿蒙 HarmonyOS 5/6 开发之路》系列图书第一作者 1485

用Multisim仿真AD630锁定放大器:从2012年电赛A题实战到避坑指南

本文详细介绍了如何使用Multisim仿真AD630锁定放大器,从2012年电赛A题实战出发,提供完整的电路搭建、参数优化及避坑指南。重点解析AD630的开关调制架构和双相位检测的工程实现,帮助读者在强噪声背景下有效提取微弱信号,提升电子设计竞赛和工程实践中的信号处理能力。

weixin_42549451的博客 203

Cesium体渲染实战:从医学CT到气象数据的3D可视化全攻略

本文详细介绍了如何利用Cesium体渲染技术实现医学CT和气象数据的3D可视化。从数据预处理到交互式渲染,涵盖了DICOM和NetCDF格式的处理技巧,以及性能优化的实战策略,帮助开发者高效处理复杂的三维数据集。

weixin_28717431的博客 420

科普文:Java基础系列之【线程池Executors类的9种创建线程池的方法及应用场景分析】

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。: 创建一个可缓存的线程池,如果线程池中的线程超过60秒没有被使用,它们将被终止并从缓存中移除。: 创建一个固定大小的线程池,其中 nThreads 指定了线程池中线程的数量。: 创建一个单线程的执行器,它创建单个工作线程来执行任务。: 创建一个固定大小的线程池,它可以根据需要创建新线程,但会按照固定延迟执行具有给定初始延迟的任务。: 创建一个工作窃取线程池,它使用多个队列,每个线程都从自己的队列中窃取任务。

为无为,事无事,味无味。 948

电力工程师必看:GOOSE/SV/MMS三协议在智能变电站中的分工与联动实例

本文深入解析了智能变电站中GOOSE、SV、MMS三大核心通信协议的分工与协同机制。GOOSE协议作为过程层的“闪电信使”,负责毫秒级传输跳闸命令等事件信息;SV协议则传输精确同步的电流电压采样值流;而MMS协议作为站控层的“管理语言”,实现设备监控与数据管理。三者基于IEC 61850标准,在过程层、间隔层和站控层之间高效联动,共同构建了智能变电站稳定可靠的通信骨架。

jj890的博客 370

讲一下创建线程池的方法及如何应用

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。在学习线程池的过程中,一定避不开Executors类,掌握这个类的使用、原理、使用场景,对于实际项目开发时,运用自如,以下是一些常用的方法:: 创建一个可缓存的线程池,如果线程池中的线程超过60秒没有被使用,它们将被终止并从缓存中移除。: 创建一个固定大小的线程池,其中 nThreads 指定了线程池中线程的数量。: 创建一个单线程的执行器,它创建单个工作线程来执行任务。: 创建一个固定大小的线程池,它可以根据需要创建新线程,

s44359487yad的博客 1146

短信验证码接收平台完整搭建教程(超详细版,附完整源码)

在网站注册、用户登录、找回密码等功能中,验证码(特别是短信验证码)已成为验证用户身份的常见方式。本教程将详细介绍如何从零搭建一个短信验证码接收平台,用于保存、展示并管理来自短信网关或中间转发程序提交的验证码数据。✅ 教程适合有基本 Python/HTML 基础的开发者🚫 禁止用于任何违法用途,仅供学习参考✅ 支持通过 POST 接口接收短信验证码✅ 支持通过网页实时查看验证码历史记录✅ 验证码信息保存在 SQLite 数据库✅ 支持时间戳排序、查询最新验证码。

weixin_52548902的博客 4567

设备树实践:从零构建一个嵌入式板级的设备树描述

本文详细介绍了如何从零开始为嵌入式Linux系统构建设备树(Device Tree),以i.MX6ULL处理器为例,涵盖硬件分析、设备树语法、外设配置及调试技巧。通过实践指导,帮助开发者掌握设备树编写,实现硬件与软件的高效对接,提升嵌入式开发效率。

plum99的博客 307

【JavaEE】Executors中常见线程池工厂方法及其使用

本文将介绍Executors中常见一些工厂方法。Executors中的工厂方法其实就是对ThreadPoolExecutor的封装,方便使用。

p_fly的博客 1159

2011-2024年间各省数字普惠金融指数数据(覆盖广度、使用深度和数字化程度)

2、指标:数字金融普惠指数、数字金融覆盖广度指数、数字金融使用深度指数、普惠金融数字化程度指数、数字金融使用深度指数下属的支付、保险、货币基金、信用服务、投资、信贷等分类指数。中国各省的数字普惠金融指数数据,从数字金融覆盖广度、数字金融使用深度和普惠金融数字化程度3个维度来构建数字普惠金融指标体系。数字普惠金融指数一共3个维度、33项具体指标。全套指数包含数字金融覆盖广度指数、数字金融使用深度指数和普惠金融数字化程度指数,以及数字金融使用深度指数下属的支付、保险、货币基金、信用服务、投资、信贷等分类指数。

【优快云】 1万+

java创建线程九种方法

java创建线程九种方法

hanjiaqian的博客 907

【Bluetooth蓝牙开发】一、开篇词 | 打造全网最详细的Bluetooth开发教程

1、开篇词 2、蓝牙开发入门 3、蓝牙协议栈总览 4、蓝牙协议栈——物理层 5、蓝牙协议栈——链路层 6、蓝牙协议栈——传输层 7、蓝牙协议栈——L2CAP协议 8、蓝牙协议栈——ATT协议 9、蓝牙协议栈——GATT协议 10、蓝牙通信流程分析 11、蓝牙协议栈——Bluez交叉编译 12、蓝牙调试工具汇总

Donge's Blog 1万+

Codex、Claude Code、Opus、Trae、Cursor:一文讲懂 2026 AI 编程工具格局

2026年AI编程工具将形成三层体系:模型层(Opus/Codex)负责思考,执行层(Claude Code/Codex Agent)负责自动开发任务,工具层(Cursor/Trae)提供AI集成的开发环境。关键区别在于Agent路线强调AI主导自动化开发,而IDE路线保持人类主导。未来竞争力取决于能否将AI整合为生产力系统,掌握模型推理、Agent任务编排和AI IDE协同三大能力。核心转变是从"写代码"升级为"指挥AI写代码",高级玩家通过组合不同层级工具构建自动

m0_51738372的博客 2697

JUC自定义自己的线程池

先来看这个Executors来创建线程池: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorsDemo { public static void main(String[] args) { // ExecutorService service = Executors.newSingleThreadExecutor();//单

qq_42388853的博客 150

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Executors提供的9种创建线程池的方法及原理分析

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。在学习线程池的过程中,一定避不开Executors类,掌握这个类的使用、原理、使用场景,对于实际项目开发时,运用自如,以下是一些常用的方法,V哥来一一细说:: 创建一个可缓存的线程池,如果线程池中的线程超过60秒没有被使用,它们将被终止并从缓存中移除。: 创建一个固定大小的线程池,其中 nThreads 指定了线程池中线程的数量。: 创建一个单线程的执行器,它创建单个工作线程来执行任务。: 创建一个固定大小的线程池,它可以根据

CTZL123456的博客 1423

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java线程池(超详细)

文章目录1.线程池概念2. JUC线程池架构3. Executors创建线程的4种方法4. 线程池的标准创建方式5. 向线程池提交任务的两种方式6. 线程池的任务调度流程7. ThreadFactory(线程工厂)8. 任务阻塞队列 1.线程池概念 创建Java线程需要给线程分配堆栈内存以及初始化内存,还需要进行系统调用,频繁地创建和销毁线程会大大降低系统的运行效率,采用线程池来管理线程有以下好处: 提升性能:线程池能独立负责线程的创建、维护和分配 线程管理:每个Java线程池会保持一些基本的线程统计信息

CodePanda@GPF的博客 5万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

为什么不推荐通过Executors直接创建线程池

它们统一缺点:不支持自定义拒绝策略。Executors 底层其实是使用的 ThreadPoolExecutor 的方式 创建的,但是使用的是 ThreadPoolExecutor 的默认策略,即 AbortPolicy。//默认策略//构造函数。

zzhongcy的专栏 3448

Java进阶之线程池Executors :用法实例(一百四十五)

本篇目的:Java线程池之Executors:用法实例基本概念Executors是Java并发包提供的线程池工厂类,用于快捷创建各类线程池,屏蔽底层ThreadPoolExecutor复杂参数。功能提供newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool等静态方法,支持不同并发策略。使用限制。

Android系统攻城狮 1127

Executors创建线程池的正确方法及源码分析

java.util.concurrent.ExecutorService; java.util.concurrent.Executors; 这两个包可以用来创建线程池,具体的方法如下: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //Excutors 工具类、3大方法 public class ThreadPoolTest { public static void main

weixin_44423896的博客 371

为什么阿里巴巴要禁用Executors创建线程池?

点击上方“方志朋”,选择“设为星标”回复”666“获取新整理的面试资料作者:何甜甜在吗juejin.im/post/5dc41c165188257bad4d9e69看阿里巴巴开发手册并发编...

方志朋的博客 127
上一篇: 一文搞懂Spring事务传播行为(Transaction Propagation)
下一篇: SpringSecurity的使用:为我们应用增加安全防护
加班不秃顶
博客等级 码龄7年 1822粉丝 599原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值