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

在Java中,Executors 类提供了多种静态工厂方法来创建不同类型的线程池。在学习线程池的过程中,一定避不开Executors类,掌握这个类的使用、原理、使用场景,对于实际项目开发时,运用自如,以下是一些常用的方法,V哥来一一细说:

  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 转换为不可配置的版本,这样调用者就不能修改它的配置。

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

1. newCachedThreadPool()

newCachedThreadPool 方法是 Java java.util.concurrent 包中的 Executors 类的一个静态工厂方法。这个方法用于创建一个可缓存的线程池,它能够根据需要创建新线程,并且当线程空闲超过一定时间后,线程会被终止并从线程池中移除。

下面是 newCachedThreadPool 方法的大致实现原理和源代码分析:

实现原理

  • 线程创建: 当提交任务到线程池时,如果线程池中的线程数少于核心线程数,会创建新的线程来执行任务。
  • 线程复用: 如果线程池中的线程数已经达到核心线程数,新提交的任务会被放入任务队列中等待执行。
  • 线程回收: 如果线程池中的线程在一定时间内(默认是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<Runnable>());
}

参数解释:

  • corePoolSize: 核心线程数,这里设置为0,表示线程池不会保留任何核心线程。
  • maximumPoolSize: 最大线程数,这里设置为 Integer.MAX_VALUE,表示理论上可以创建无限多的线程。
  • keepAliveTime: 当线程数大于核心线程数时,多余的空闲线程能等待新任务的最长时间,这里设置为60秒。
  • unit: keepAliveTime 参数的时间单位,这里是秒。
  • workQueue: 一个任务队列,这里使用的是 SynchronousQueue,它是一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的移除操作。

实现过程

  • 初始化: 当调用 newCachedThreadPool 时,会创建一个 ThreadPoolExecutor 实例。
  • 任务提交: 当任务提交给线程池时,线程池会检查是否有空闲线程可以立即执行任务。
  • 线程创建: 如果没有空闲线程,并且当前线程数小于 maximumPoolSize,则创建新线程执行任务。
  • 任务队列: 如果当前线程数已经达到 maximumPoolSize,则将任务放入 SynchronousQueue 中等待。
  • 线程复用: 当一个线程执行完任务后,它不会立即终止,而是尝试从 SynchronousQueue 中获取新任务。
  • 线程回收: 如果线程在 keepAliveTime 时间内没有获取到新任务,它将被终止。

这种设计使得 newCachedThreadPool 非常适合处理大量短生命周期的任务,因为它可以动态地调整线程数量以适应任务负载的变化。然而,由于它可以创建无限多的线程,如果没有适当的任务队列来控制任务的数量,可能会导致资源耗尽。因此,在使用 newCachedThreadPool 时,需要谨慎考虑任务的特性和系统的资源限制。

使用场景:

适用于执行大量短期异步任务,尤其是任务执行时间不确定的情况。例如,Web服务器处理大量并发请求,或者异步日志记录。

2. newFixedThreadPool(int nThreads)

newFixedThreadPool(int nThreads) 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法。这个方法用于创建一个固定大小的线程池,它能够确保线程池中始终有固定数量的线程在工作。

以下是 newFixedThreadPool 方法的实现原理、源代码分析以及实现过程:

实现原理

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

源代码分析

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

public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, // 核心线程数nThreads, // 最大线程数0L,      // 线程空闲时间,这里设置为0,表示线程不会空闲TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>() // 使用阻塞队列来存储任务);
}

参数解释:

  • corePoolSize: 核心线程数,这里设置为 nThreads,表示线程池中始终有 nThreads 个线程。
  • maximumPoolSize: 最大线程数,这里也设置为 nThreads,表示线程池的线程数量不会超过 nThreads。
  • keepAliveTime: 当线程数大于核心线程数时,多余的空闲线程能等待新任务的最长时间,这里设置为0,表示如果线程池中的线程数超过核心线程数,这些线程将立即终止。
  • unit: keepAliveTime 参数的时间单位,这里是毫秒。
  • workQueue: 一个任务队列,这里使用的是 LinkedBlockingQueue,它是一个基于链表的阻塞队列,可以存储任意数量的任务。

实现过程

  • 初始化: 当调用 newFixedThreadPool 时,会创建一个 ThreadPoolExecutor 实例。
  • 任务提交: 当任务提交给线程池时,线程池会检查是否有空闲的核心线程可以立即执行任务。
  • 任务队列: 如果所有核心线程都在忙碌状态,新提交的任务将被放入 LinkedBlockingQueue 中等待。
  • 线程复用: 核心线程执行完一个任务后,会尝试从 LinkedBlockingQueue 中获取新任务继续执行。
  • 线程数量控制: 由于 keepAliveTime 设置为0,当线程池中的线程数超过核心线程数时,这些线程会立即终止,从而保证线程池中的线程数量不会超过 nThreads。

这种设计使得 newFixedThreadPool 非常适合处理大量且持续的任务,因为它可以保证任务以固定的线程数量并行执行,同时避免了线程数量的无限制增长。然而,由于线程池的大小是固定的,如果任务提交的速率超过了线程池的处理能力,可能会导致任务在队列中等待较长时间。因此,在使用 newFixedThreadPool 时,需要根据任务的特性和预期的负载来合理设置 nThreads 的值。

使用场景:

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

3. newSingleThreadExecutor()

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

以下是 newSingleThreadExecutor 方法的实现原理、源代码分析以及实现过程:

实现原理

  • 单线程执行: 线程池中只有一个线程,所有任务都由这个线程顺序执行。
  • 任务队列: 如果这个线程在执行任务时有新任务提交,新任务会被放入一个阻塞队列中等待执行。
  • 线程复用: 这个线程会重复利用,执行完一个任务后,会立即尝试从队列中获取下一个任务执行。

源代码分析

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

public static ExecutorService newSingleThreadExecutor() {return new ThreadPoolExecutor(1, // 核心线程数1, // 最大线程数0L, TimeUnit.MILLISECONDS, // 线程空闲时间,这里设置为0,表示线程不会空闲new LinkedBlockingQueue<Runnable>() // 使用阻塞队列来存储任务);
}

参数解释:

  • corePoolSize: 核心线程数,这里设置为1,表示线程池中始终有一个核心线程。
  • maximumPoolSize: 最大线程数,这里也设置为1,表示线程池的线程数量不会超过1。
  • keepAliveTime: 线程空闲时间,这里设置为0,表示如果线程空闲,它将立即终止。
  • unit: keepAliveTime 参数的时间单位,这里是毫秒。
  • workQueue: 一个任务队列,这里使用的是 LinkedBlockingQueue,它是一个无界队列,可以存储任意数量的任务。

实现过程

  • 初始化: 当调用 newSingleThreadExecutor 时,会创建一个 ThreadPoolExecutor 实例。
  • 任务提交: 当任务提交给线程池时,如果核心线程空闲,则立即执行任务;如果核心线程忙碌,则将任务放入 LinkedBlockingQueue 中等待。
  • 顺序执行: 由于只有一个线程,所有任务都将按照提交的顺序被执行。
  • 任务队列: 如果核心线程在执行任务,新提交的任务将被放入 LinkedBlockingQueue 中排队等待。
  • 线程复用: 核心线程执行完一个任务后,会尝试从 LinkedBlockingQueue 中获取新任务继续执行。
  • 线程数量控制: 由于 keepAliveTime 设置为0,核心线程在没有任务执行时会立即终止。但由于 corePoolSize 和 maximumPoolSize 都为1,线程池会立即重新创建一个线程。

这种设计使得 newSingleThreadExecutor 非常适合处理需要保证任务顺序的场景,例如,当任务之间有依赖关系或者需要按照特定顺序执行时。同时,由于只有一个线程,这也避免了多线程环境下的并发问题。然而,由于只有一个线程执行任务,这也限制了并行处理的能力,如果任务执行时间较长,可能会导致后续任务等待较长时间。因此,在使用 newSingleThreadExecutor 时,需要根据任务的特性和对顺序的要求来决定是否适用。

使用场景:

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

4. newScheduledThreadPool(int corePoolSize)

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

以下是 newScheduledThreadPool 方法的实现原理、源代码分析以及实现过程:

实现原理

  • 定时任务: 线程池能够按照指定的延迟执行任务,或者以固定间隔周期性地执行任务。
  • 固定线程数: 线程池中的线程数量被限制为 corePoolSize 指定的大小。
  • 任务队列: 任务首先由核心线程执行,如果核心线程都在忙碌状态,新任务将被放入一个延迟任务队列中等待执行。

源代码分析

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

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 以满足性能要求。

使用场景:

适用于需要定期执行任务或在将来某个时间点执行任务的场景。例如,定时备份数据、定时发送提醒等。

5. newWorkStealingPool(int parallelism)

newWorkStealingPool 是 Java 8 中新增的 java.util.concurrent 包的 Executors 类的一个静态工厂方法。这个方法用于创建一个工作窃取(Work-Stealing)线程池,它能够提高并行任务的执行效率,特别是在多处理器系统上。

实现原理

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

源代码分析

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

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 创建的线程池非常适合于需要高并发和高吞吐量的场景,尤其是在多处理器系统上。然而,由于工作窃取机制,它可能不适用于任务执行时间非常短或者任务数量非常少的场景,因为窃取任务本身可能会引入额外的开销。

使用场景:

适用于工作量不均匀或可分解为多个小任务的并行计算任务。例如,图像处理、数据分析等,可以在多核处理器上有效利用所有核心。

6. newSingleThreadScheduledExecutor()

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

以下是 newSingleThreadScheduledExecutor 方法的实现原理、源代码分析以及实现过程:

实现原理

  • 单线程执行: 执行器确保所有任务都在单个线程中顺序执行,这保证了任务的执行顺序。
  • 定时任务: 支持延迟执行和周期性执行任务。
  • 任务队列: 所有任务首先被放入一个任务队列中,然后由单线程按顺序执行。

源代码分析

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

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

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

ScheduledThreadPoolExecutor 内部使用了一个 DelayedWorkQueue 作为任务队列,这个队列能够按照任务的预定执行时间对任务进行排序。

实现过程

  • 初始化: 当调用 newSingleThreadScheduledExecutor 时,会创建一个 ScheduledThreadPoolExecutor 实例,其核心线程数为1。
  • 任务提交: 当任务提交给执行器时,任务会被封装成 ScheduledFutureTask 或者 RunnableScheduledFuture,然后放入 DelayedWorkQueue 中。
  • 任务调度: 单线程会不断地从 DelayedWorkQueue 中获取任务,并按照预定的时间执行。如果任务的执行时间已经到达,任务将被执行;如果还没有到达,线程会等待直到执行时间到来。
  • 顺序执行: 由于只有一个线程,所有任务都将按照它们被提交的顺序被执行。
  • 周期性任务: 对于需要周期性执行的任务,执行器会在每次任务执行完毕后,重新计算下一次执行的时间,并再次将任务放入队列。

特点

  • newSingleThreadScheduledExecutor 创建的执行器非常适合需要保证任务顺序的场景,例如,需要按照特定顺序执行的任务或者具有依赖关系的任务。
  • 它也适合执行定时任务,如定期执行的维护任务或者后台任务。
  • 由于只有一个线程,这也避免了多线程环境下的并发问题,简化了任务同步和状态管理。

使用 newSingleThreadScheduledExecutor 创建的执行器可以提供强大的定时任务功能,同时保持任务执行的顺序性。然而,由于只有一个线程执行任务,这也限制了并行处理的能力,如果任务执行时间较长,可能会导致后续任务等待较长时间。因此,在使用 newSingleThreadScheduledExecutor 时,需要根据任务的特性和对顺序的要求来决定是否适用。

使用场景:

适用于需要单个后台线程按计划执行任务的场景。例如,定时检查系统状态、定时执行维护任务等。

7. privilegedThreadFactory()

privilegedThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个线程工厂,该工厂能够产生具有特权访问的线程。这意味着这些线程可以加载系统属性和库,并且可以访问文件系统。

以下是 privilegedThreadFactory 方法的实现原理、源代码分析以及实现过程:

实现原理

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

源代码分析

在 Java 的标准库中,privilegedThreadFactory 方法的实现细节并未公开,因为它是一个私有方法。然而,我们可以分析其大致工作原理。privilegedThreadFactory 方法的调用示例如下:

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 的具体实现,但是我们可以提供一个示例实现,以展示如何创建具有特权访问的线程:

public class PrivilegedThreadFactory implements ThreadFactory {@Overridepublic Thread newThread(Runnable r) {return AccessController.doPrivileged(new PrivilegedAction<>() {@Overridepublic Thread run() {return new Thread(r);}});}
}

在这个示例中,PrivilegedAction 是一个实现了 PrivilegedAction<T> 接口的匿名类,其 run 方法创建了一个新的线程。AccessController.doPrivileged 方法用于执行一个特权操作,这里是为了确保线程创建过程中具有必要的权限。

特点

  • 使用 privilegedThreadFactory 创建的线程可以在需要访问敏感系统资源的情况下使用。
  • 这种线程工厂通常用于需要执行特权操作的应用程序,例如,访问系统属性或者执行文件 I/O 操作。

使用 privilegedThreadFactory 可以确保线程在执行任务时具有适当的安全权限,从而避免安全异常。然而,需要注意的是,过度使用特权访问可能会带来安全风险,因此在设计应用程序时应谨慎使用。

使用场景:

适用于需要线程具有更高权限来访问系统资源的场景。例如,需要访问系统属性或执行文件I/O操作的应用程序。

8. defaultThreadFactory()

defaultThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个默认的线程工厂。这个线程工厂生成的线程没有特殊的权限,它们是普通的线程,具有标准的访问权限。

以下是 defaultThreadFactory 方法的实现原理、源代码分析以及实现过程:

实现原理

  • 标准线程创建: 创建的线程工厂将生成具有默认属性的线程。
  • 线程名称: 生成的线程具有默认的线程名称前缀,通常是 “pool-x-thread-y”,其中 x 和 y 是数字。
  • 线程优先级: 线程的优先级设置为 Thread.NORM_PRIORITY,这是 Java 线程的默认优先级。
  • 非守护线程: 创建的线程不是守护线程(daemon threads),它们的存在不会阻止 JVM 退出。

源代码分析

Java 的 defaultThreadFactory 方法的具体实现细节并未完全公开,因为它是 Executors 类的一个私有静态方法。但是,我们可以根据 Java 的 ThreadFactory 接口和一些公开的源代码片段来分析其大致实现。

以下是 defaultThreadFactory 方法的调用示例:

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

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

实现过程

  • 初始化: 当调用 defaultThreadFactory 方法时,会返回一个新的 DefaultThreadFactory 实例。
  • 线程创建: 使用这个工厂创建线程时,它会调用 newThread(Runnable r) 方法。
  • 设置线程名称: 在 newThread(Runnable r) 方法的实现中,会创建一个新的 Thread 对象,并设置一个默认的线程名称。
  • 设置线程组: 新线程会被分配到一个默认的线程组中。
  • 线程优先级和守护状态: 线程的优先级设置为默认值,且线程不是守护线程。

示例代码

虽然我们不能查看 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。

特点

  • 使用 defaultThreadFactory 创建的线程工厂生成的线程具有标准的 Java 线程属性。
  • 这种线程工厂通常用于不需要特殊权限的应用程序。
  • 由于线程不是守护线程,它们的存在可以维持 JVM 的运行,直到所有非守护线程执行完毕。

使用 defaultThreadFactory 可以确保线程在执行任务时具有标准的安全和执行属性,适合大多数常规用途。然而,如果应用程序需要特殊的线程属性,如守护线程或不同的优先级,可能需要自定义线程工厂。

使用场景:

适用于大多数标准应用程序,需要创建具有默认属性的线程。这是大多数 ExecutorService 实现的默认选择。

9. unconfigurableExecutorService(ExecutorService executor)

unconfigurableExecutorService 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法。这个方法用于创建一个不可配置的 ExecutorService 包装器,这意味着一旦包装后的 ExecutorService 被创建,就不能更改其配置,比如不能修改其线程池大小或任务队列等。

以下是 unconfigurableExecutorService 方法的实现原理、源代码分析以及实现过程:

实现原理

  • 封装: 将现有的 ExecutorService 封装在一个不可配置的代理中。
  • 不可修改: 所有修改配置的方法调用,如 shutdown, shutdownNow, setCorePoolSize 等,都将抛出 UnsupportedOperationException。
  • 转发: 除了配置修改的方法外,其他方法调用将被转发到原始的 ExecutorService。

源代码分析

unconfigurableExecutorService 方法的具体实现细节并未完全公开,因为它是 Executors 类的一个私有静态方法。但是,我们可以根据 Java 的 ExecutorService 接口和代理机制来分析其大致实现。

以下是 unconfigurableExecutorService 方法的调用示例:

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

这里,FinalizableDelegatedExecutorService 是 Executors 类的一个私有静态内部类,它实现了 ExecutorService 接口,并代理了对另一个 ExecutorService 的调用。

实现过程

  • 初始化: 当调用 unconfigurableExecutorService 方法时,会返回一个新的 FinalizableDelegatedExecutorService 实例,它将原始的 ExecutorService 作为参数。
  • 方法调用拦截: 对 FinalizableDelegatedExecutorService 的方法调用将首先被拦截。
  • 配置修改拦截: 如果调用的方法是用于修改配置的,比如 shutdown 或 shutdownNow,将抛出 UnsupportedOperationException。
  • 转发其他调用: 对于其他不涉及配置修改的方法调用,比如 submit, execute, 将被转发到原始的 ExecutorService。

示例代码

下面V哥来模拟一个示例实现,以展示如何创建一个不可配置的 ExecutorService 代理:

public class UnconfigurableExecutorService implements ExecutorService {private final ExecutorService executor;public UnconfigurableExecutorService(ExecutorService executor) {this.executor = executor;}@Overridepublic void shutdown() {throw new UnsupportedOperationException("Shutdown not allowed");}@Overridepublic List<Runnable> shutdownNow() {throw new UnsupportedOperationException("Shutdown not allowed");}@Overridepublic boolean isShutdown() {return executor.isShutdown();}@Overridepublic boolean isTerminated() {return executor.isTerminated();}@Overridepublic void execute(Runnable command) {executor.execute(command);}// 其他 ExecutorService 方法的实现,遵循相同的模式
}

在这个示例中,UnconfigurableExecutorService 拦截了 shutdown 和 shutdownNow 方法,并抛出了异常。其他方法则直接转发到原始的 ExecutorService。

特点

  • 使用 unconfigurableExecutorService 创建的 ExecutorService 代理确保了线程池的配置不能被外部修改。
  • 这可以用于防止意外地更改线程池的状态,提高线程池使用的安全性。
  • 除了配置修改的方法外,其他所有方法都保持了原有 ExecutorService 的行为。

使用 unconfigurableExecutorService 可以为现有的 ExecutorService 提供一个安全层,确保它们的状态不会被意外地更改。这对于在多线程环境中共享 ExecutorService 时特别有用。

使用场景:

适用于需要确保线程池配置在创建后不被更改的场景。例如,当多个组件共享同一个线程池时,可以防止一个组件意外修改配置。

最后

以上是V哥在授课过程中整理的关于Executors 9种创建线程池的方法及原理分析,分享给大家,希望对正在学习 Java 的你有所帮助,每天分享技术干货,欢迎关注威哥爱编程,你的鼓励是V哥技术创作路上的助推器,不喜勿喷,感谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/868959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

User parameters 用户参数与Web监控

目录 一. 自定义键介绍 二. 制作步骤 1. 添加无可变部分参数 2. 添加有可变参数 3. 使用用户参数监控php-fpm 服务的状态 三. Web页面导入应用监控 四. Web监控 主要功能和操作&#xff1a; 开启方式 官方预定义监控项文档https://www.zabbix.com/documentation/6…

华三m-lag三层转发+VRRP配置案例

目录 一、相关理论介绍 1.1 华三M-LAG介绍 1.2 DRCP协议 1.3 keepalive机制 1.4 MAD机制 1.5 一致性检查功能 二、M-LAG系统建立及工作过程 三、实验组网案例 3.1 组网需求 3.2 组网拓扑 3.3 设备接口及地址规划 四、具体配置命令 4.1 S6850-1的配置 4.2 S6850-2…

华为OD机考题(HJ71 字符串通配符)

前言 经过前期的数据结构和算法学习&#xff0c;开始以OD机考题作为练习题&#xff0c;继续加强下熟练程度。 描述 问题描述&#xff1a;在计算机中&#xff0c;通配符一种特殊语法&#xff0c;广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的…

AI:助力开发者翱翔,而非抢夺其舞台

在当今这个科技飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;犹如一股春风&#xff0c;悄然渗透进全球各个行业&#xff0c;尤其在软件开发领域&#xff0c;其影响力日益显著。从初创企业到跨国巨头&#xff0c;无一不在积极探索AI如何重塑编程的面貌&#xf…

护眼灯什么价位的好?好用又实惠的护眼灯推荐

护眼灯&#xff0c;简单来说就是保护视力的台灯&#xff0c;专业的护眼台灯的光线与自然光光线相似&#xff0c;有亮度稳定、不闪烁&#xff0c;发光面积大等这些特点。那么&#xff0c;护眼灯什么价位的好&#xff1f;市面上所出现的护眼台灯良莠不齐&#xff0c;价格低的质量…

Spring容器加载Bean和JVM加载类

1、JVM加载类 类的加载是在首次需要访问类的信息或实例化类的对象时发生的过程。ClassLoader负责加载类的字节码&#xff0c;并在内存中创建对应的Class对象&#xff0c;从而使得Java程序能够操作和使用这些类。 在Java中&#xff0c;类的加载是按需进行的&#xff0c;也就是…

【源码+文档+调试讲解】文物管理系统

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

warning: LF will be replaced by CRLF the next time Git touches it warning

问题&#xff1a; warning: in the working copy of , LF will be replaced by CRLF the next time Git touches it warning: 今天上传git时报错&#xff0c;使用Ai&#xff1b;得知&#xff1b; 解决&#xff1a; 将 Git 配置为不自动转换换行符&#xff0c;使用以下命令…

一.5 高速缓存至关重要

这个简单的示例揭示了一个重要的问题&#xff0c;即系统花费了大量的时间把信息从一个地方挪到另一个地方。hello程序的机器指令最初是存放在硬盘上&#xff0c;当程序加载时&#xff0c;它们被复制到主存&#xff1b;当处理器运行程序时&#xff0c;指令又从主存复制到处理器。…

触摸屏虚拟键盘组件 jQuery Virtual Keyboard使用 自定义键盘

如何在触摸设备上为输入域添加虚拟键盘&#xff1f; 一个插件可以解决这个问题&#xff0c;关键还支持高度自定义&#xff08;git地址&#xff09;&#xff1a; GitHub - Mottie/Keyboard: Virtual Keyboard using jQuery ~ 官网地址&#xff1a;Virtual Keyboard 使用步骤&…

大模型学习笔记0-前言

在当前人工智能技术快速发展的背景下&#xff0c;大模型成为了这个领域最为耀眼的焦点之一。它们不仅在机器学习的很多子领域表现突出&#xff0c;还在众多实际应用中展现出巨大的潜力与价值。因此&#xff0c;对于任何致力于深入了解当代AI技术的学习者来说&#xff0c;掌握大…

3. 积分之迷

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小明开了个网上商店&#xff0c;卖风铃。共有 33 个品牌&#xff1a;&#x1d434;&#xff0c;&#x1d435;&#xff0c;&#x1d436;A&#xff0c;B&#xff0c…

NISP证书备考指南与经验分享

在信息安全领域&#xff0c;NISP(国家信息安全水平考试)作为衡量专业能力的重要标尺&#xff0c;不仅是职场晋升的敲门砖&#xff0c;更是个人技能提升的关键一步。面对这一挑战&#xff0c;如何高效备考&#xff0c;成为众多学员关注的焦点。今天&#xff0c;为您精心打造这份…

原生APP开发的优势

原生APP开发是指使用特定的编程语言和开发工具&#xff0c;针对特定的操作系统平台进行开发的应用程序。相比于混合开发和Web开发&#xff0c;原生APP开发具有以下优势。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 性能更优 原…

Python和numpy相关函数

目录 一、python中变量前面有个*二、numpy.prod三、numpy.savez四、 arr[..., ::-1]五、 yield六、 string.join()七、 numpy.random.choice() 一、python中变量前面有个* 在Python中&#xff0c;变量前面的星号&#xff08;*&#xff09;有多种用法&#xff0c;主要与函数参数…

Spring Boot Vue 毕设系统讲解 3

目录 项目配置类 项目中配置的相关代码 spring Boot 拦截器相关知识 一、基于URL实现的拦截器&#xff1a; 二、基于注解的拦截器 三、把拦截器添加到配置中&#xff0c;相当于SpringMVC时的配置文件干的事儿&#xff1a; 项目配置类 项目中配置的相关代码 首先定义项目认…

实例分割:深度学习在图像识别中的突破

标题&#xff1a;实例分割&#xff1a;深度学习在图像识别中的突破 实例分割是计算机视觉领域中的一项高级任务&#xff0c;它不仅识别图像中的多个对象&#xff0c;还精确地定位每个对象的边界。这项技术是目标检测和语义分割的结合&#xff0c;能够区分同类对象中的个体差异…

【亲测解决】No module named _cffi_backend

微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xff0c;代码随想随记 GitHub: https://github.com/riverind 抖音【暂未开始&#xff0c;计划开始】&#xf…

FPGA之 串口UART通信

FPGA之串口UART通信 1. UART发送模块&#xff08;Transmitter&#xff09;2. UART接收模块&#xff08;Receiver&#xff09;3. testbench4. 边沿检测电路 串口&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c;UART&#xff09;是一种串行通信协议&#x…

通义千问Qwen-VL-Chat大模型本地部署(一)

目录 前言 环境准备 软件安装 其它库安装启动项目 FASTAPI 小结 前言 人工智能大模型是一种能够利用大数据和神经网络来模拟人类思维和创造力的人工智能算法。它利用海量的数据和深度学习技术来理解、生成和预测新内容&#xff0c;通常情况下有数十亿乃至数百亿个参数&#xf…