为什么不建议使用Executors来创建线程池?

Executors是一个工厂类,它提供了一系列用于创建不同类型的线程池的静态方法。尽管 Executors 提供了一种快速创建线程池的方式,但有一些原因不建议直接使用它来创建线程池,尤其是在生产环境中的长期运行应用程序中。

以下是一些不建议直接使用 Executors 创建线程池的原因:

  1. 缺乏对线程池的精细控制Executors 提供的方法通常创建一些简单的线程池,如固定大小的线程池、单线程线程池等。然而,这些线程池的配置通常是有限制的,难以进行进一步的定制和优化。

  2. 可能引发内存泄漏:一些 Executors 创建的线程池,特别是 FixedThreadPoolSingleThreadExecutor,使用无界队列来存储等待执行的任务。这意味着如果任务提交速度远远快于任务执行速度,队列中可能会积累大量未执行的任务,可能导致内存泄漏。

  3. 不易处理异常Executors 创建的线程池默认使用一种默认的异常处理策略,通常只会将异常打印到标准输出或记录到日志中,但不会提供更多的控制。这可能导致异常被忽略或无法及时处理。

  4. 不支持线程池的动态调整:某些线程池应该支持动态调整线程数量以应对不同的负载情况。Executors 创建的线程池通常是固定大小的,不容易进行动态调整。

  5. 可能导致不合理的线程数目:一些 Executors 方法创建的线程池默认将线程数目设置为非常大的值,这可能导致系统资源的浪费和性能下降。

因此,对于生产环境中的应用程序,通常建议使用 ThreadPoolExecutor 类直接创建和配置线程池,以便更精确地控制线程池的各个参数,包括核心线程数、最大线程数、队列类型、拒绝策略等。这样可以更好地满足应用程序的需求,并确保线程池在不同负载情况下表现良好。当然,在某些情况下,Executors 创建的简单线程池可能足够使用,但需要谨慎考虑其限制和适用性。

创建newSingleThreadExecutorFiexdThreadPool时都会有

建议直接使用ThreadPoolExecutor来进行创建线程

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        // 配置线程池参数
        int corePoolSize = 2; // 核心线程数
        int maximumPoolSize = 4; // 最大线程数
        long keepAliveTime = 60; // 非核心线程的闲置超时时间(单位:秒)
        int queueCapacity = 10; // 队列容量

        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(queueCapacity)
        );

        // 提交任务给线程池执行
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

在上面的示例中,我们首先配置了线程池的参数,包括核心线程数、最大线程数、非核心线程的闲置超时时间以及队列容量。然后,我们使用这些参数创建了一个 ThreadPoolExecutor 实例。

接下来,我们使用 execute() 方法提交任务给线程池执行。每个任务都是一个简单的输出语句,显示任务编号和执行任务的线程名称。

最后,我们调用 shutdown() 方法来关闭线程池。注意,在关闭线程池之前,应该等待任务执行完毕,以确保所有任务都得到执行。

通过直接使用 ThreadPoolExecutor,你可以更精确地控制线程池的参数,以满足你的应用程序需求。这包括核心线程数、最大线程数、闲置线程的超时时间、队列类型和拒绝策略等。可以根据具体需求来进行配置。

new ArrayBlockingQueue<>(queueCapacity) 是用于创建一个带有固定容量的阻塞队列的代码片段。

让我解释一下这个代码的含义:

  • ArrayBlockingQueue 是 Java 中的一个阻塞队列实现,它是一个基于数组的有界队列。这意味着队列的容量是有限的,不能超过指定的容量。

  • queueCapacity 是你指定的队列容量,它决定了队列可以容纳的元素数量。在 ArrayBlockingQueue 中,一旦队列达到了指定容量,任何试图插入更多元素的操作都会被阻塞,直到有空间可用。

  • new ArrayBlockingQueue<>(queueCapacity) 表示创建一个具有指定容量的 ArrayBlockingQueue 实例。这个队列将在达到容量时阻塞插入操作,直到队列中有元素被取出为止。

这种类型的队列通常用于多线程编程中,其中一个线程生产数据并将其放入队列,而另一个线程消费队列中的数据。阻塞队列可以用于线程间的协调,使得生产者和消费者之间可以安全地进行数据交换。

需要注意的是,ArrayBlockingQueue 是有界队列,一旦达到容量限制,插入操作将被阻塞。如果需要一个无界队列,可以考虑使用 LinkedBlockingQueue。选择队列类型取决于应用程序的需求和设计。

posted @ 2023-09-19 10:07  CYF0913  阅读(1053)  评论(0)    收藏  举报