线程池相关-线程池的创建

本文学习过程中持续更新

线程池的创建

为什么不推荐用 Executors 创建线程池,而是用 ThreadPoolExecutor 的方式?

通过 Executors 创建,以 FixedThreadPool 举例

ExecutorService exec = Executors.newFixedThreadPool(thread_num)

public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
}

会创建一个 ThreadPoolExecutor

在这里的构造方法里,传5个参数,参数如下:

  • corePoolSize: 除非设置了 允许核心线程超时的最大时间,否则即使线程处于空闲状态,也要保留在池中的线程数
  • maximumPoolSize: 线程池维护线程的最大数量
  • keepAliveTime: 当线程数大于核心时,这是多余空闲线程在终止之前等待新任务的最长时间
  • unit: 上述时间的单位
  • workQueue: 在执行任务之前用于保存任务的缓冲队列,此队列仅包含 excute 方法提交的 Runnable 任务

当然,这只是 ThreadPoolExecutor 的构造方法之一,ThreadPoolExecutor 还有更多参数的构造方法,这里先不谈。

再来到 excute 方法,该方法用于执行提交的 Runnable 任务。

excute 的执行逻辑大概如下:

  • 当池中线程数量少于 corePoolSize,创建一个新线程去执行任务
  • 当池中线程数量等于 corePoolSize,如果缓冲队列 workQueue 未满,放入缓冲队列
  • 如果缓冲队列满,线程池数量小于 maximumPoolSize, 建新的线程来处理任务
  • 如果创建新的线程来处理任务失败,说明线程池数量饱和,即达到了maximumPoolSize,会拒绝这次任务

ThreadPoolExecutor 中,还有一个 RejectedExecutionHandler 用来对拒绝的任务进行处理,这里先不谈。

excute 的执行方法中可以看到,缓冲队列也有个饱和值,这个值是可以由我们自己设置的。

举个例子,直接使用 ThreadPoolExecutor 创建的时候:

int count = 100;
ThreadPoolExecutor tp = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(count));

而上文中,用 Executors 创建 FixedThreadPool


ExecutorService exec = Executors.newFixedThreadPool(thread_num)

public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
}

这里的缓冲队列初始化方法是这样的:

/**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

允许缓冲队列的长度为 Integer.MAX_VALUE,这时可能会堆积大量的请求,有OOM风险。
创建 SingleThreadExecutor与之同理,存在缓冲队列堆积大量请求风险。

Executors 创建 CachedThreadPool :

ExecutorService exec = Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

允许创建的线程数量为 Integer.MAX_VALUE,会堆积大量的线程,有OOM风险。
创建 ScheduledThreadPool 与之同理,有堆积大量线程的风险。

所以推荐用 ThreadPoolExecutor 的方式创建线程池,手动设置各种参数(包括最大线程数、缓冲队列的最大长度)。

你可能感兴趣的:(线程池相关-线程池的创建)