线程池的3个方法,7大参数,4种拒绝策略

线程池的3个方法,7大参数,4种拒绝策略

一、3个创建线程池的方法

1. Executors.newSingleThreadExecutor(); // 单个线程的线程池
2. Executors.newFixedThreadPool(5); // 固定大小的线程池
3. Executors.newCachedThreadPool(); // 可伸缩的线程池,遇强则强,遇弱则弱

阿里开发手册中:

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

以下为三种创建方法的函数的源码:

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

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

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

以下就是 ThreadPoolExecutor 函数,有7大参数,下节详细阐述。

二、线程池的7大参数

(1) corePoolSize:核心线程数(当等待队列不满的时候,只会调用核心线程)

(2) maximumPoolSize:最大线程数(当等待队列满了的时候,会激活更多的线程)

(3) keepAliveTime:新激活的线程在没有任务的情况下的存活时间

(4) TimeUnit:keepAliveTime 的单位

(5) BlockingQueue:阻塞队列,当核心线程满了,新的请求会在队列中等待

(6) ThreadFactory:创建线程的工厂,使用默认的 Executors.defaultThreadFactory() 即可

(7) RejectedExecutionHandler:拒绝策略,当超出线程池的最大承载时的拒绝策略(有4种拒绝策略)

现在我们再来看看阿里开发手册中的两句话:

1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

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

newSingleThreadExecutor 中的传入的阻塞队列参数为 new LinkedBlockingQueue<Runnable>() ,而 LinkedBlockingQueue 的默认构造函数的队列长度为 Integer.MAX_VALUE 约等于21亿,所以可能会堆积大量的请求,从而导致 OOM。FixedThreadPool同理。

2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

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

newCachedThreadPool 中的传入的最大线程数参数为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

那么最大线程数应该如何定义呢?分以下两种情况:

  1. CPU 密集型:几个核心就定义为几,可以保证 CPU 的效率最高,例如一个4核的 CPU,则可以令 maximumPoolSize = 4,其中可以使用 Runtime.getRuntime().availableProcessors() 获取 CPU 的核数。
  2. IO 密集型:程序中有很多大型任务,IO 十分占用资源!此时要判断程序中十分消耗 IO 的线程数,只要大于该数即可,一般可以设置为两倍。

三、4种拒绝策略

(1) ThreadPoolExecutor.AbortPolicy():当达到线程池的最大承载时,还有请求,则不处理,并且抛出异常。

(2) ThreadPoolExecutor.CallerRunsPolicy(): 当达到线程池的最大承载时,还有请求,那条线程请求那条线程去处理,一般为 main 线程。

(3) ThreadPoolExecutor.DiscardPolicy(): 当达到线程池的最大承载时,还有请求,则丢掉任务,并且不抛出异常。

(4) ThreadPoolExecutor.DiscardOldestPolicy(): 当达到线程池的最大承载时,还有请求,则尝试去和最老的线程竞争, 并且不抛出异常。

posted @ 2021-01-17 21:05  xiekun96  阅读(631)  评论(0编辑  收藏  举报