强制禁止,线程池不允许使用Executors创建

在这里插入图片描述

目录

    • 一、线程池
    • 二、线程池不允许使用Executors创建
    • 三、这是为什么呢?
    • 四、下面通过一段代码,测试一下。
    • 五、线程池参数

大家好,我是哪吒。

一、线程池

在程序开发中,高并发场景越来越多,线程池首当其冲。

简单回顾一下:

  1. 单线程池newSingleThreadExecutor(),只有一个核心线程的线程池,保证任务按FIFO顺序一个个执行;
  2. 固定线程数线程池newFixedThreadPool(10),固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行;
  3. 可缓存线程池newCachedThreadPool(),创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式,它只适用于生命周期短的任务;
  4. 固定线程数newScheduledThreadPool(10),支持定时和周期性任务newScheduledThreadPool(10),顾名思义,在固定线程数的前提下,添加了定时任务。

二、线程池不允许使用Executors创建

《阿里巴巴 Java 开发手册》强制禁止,线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能让编写代码的工程师更加明确线程池的运行规则,避免资源耗尽的风险。

Executors返回的线程池对象的弊端如下:

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

强制禁止,线程池不允许使用Executors创建_第1张图片

好吧,昨天,我还通过ExecutorService executorService = Executors.newFixedThreadPool(200);创建了一个线程池,看来以后要注意了。

三、这是为什么呢?

具体分析以下【说明】中的内容,以FixedThreadPool为例,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

也就是说,我建了一个固定长度为200的线程池,当请求超过200时,线程池会新建一个队列queue,来储存阻塞的线程,而且new LinkedBlockingQueue()是一个没有大小限制的。规约的意思是当请求过多,每个任务执行较慢时,远超线程池的大小,会全部压到这个无界Queue中,这个队列queue会扛不住,有点道理。

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

四、下面通过一段代码,测试一下。

public class ThreadPoolTest {
    public static void main(String[] args) throws IOException {
        ExecutorService executorService = Executors.newFixedThreadPool(200);
        for (int i = 0; i < 1000000; i++) {
            Thread0926 thread = new Thread0926();
            executorService.execute(thread);
        }
    }

    private static Object lock = new Object();
    public void increase() {
        synchronized (lock) {
            try {
                String content = FileUtils.readFileToString(new File("H:\\CSDN\\08Typora文章\\Java高并发\\Java高并发编程实战1,那些年学过的锁.md"));
                // 为了模拟OOM问题,歇20分钟
                TimeUnit.MINUTES.sleep(20);
            } catch (Exception e) {
            }
        }
    }
}

果然,运行一段时间后,OOM了。

[09:49:07.412] [http-nio-48135-exec-1] [ERROR].[dispatcherServlet]:175 ] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: unable to create new native thread] with root cause

java.lang.OutOfMemoryError: unable to create new native thread 

从日志可以看出,java.lang.OutOfMemoryError: unable to create new native thread ,OOM错误,无法新建本地线程。

由于代码中为了模拟OOM问题,读取一次超大文件,然后歇20分钟,也就是说会有大量的线程阻塞。

五、线程池参数

工作中,一般会根据程序的实际使用场景,评估线程池的几个重要参数:

  1. corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
  2. maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
  3. keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
  4. workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
  5. threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
  6. handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。

设置拒绝策略,确保线程池的工作行为符合需求,一般都需要设置有界的工作队列和可控的线程数。

还有一点需要注意,当创建线程或线程池时,要指定有意义的线程名字,方便排查问题,定位错误代码位置。

上一篇:一个关于 i++ 和 ++i 的面试题打趴了所有人

哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

你可能感兴趣的:(搬砖工逆袭Java架构师,java,开发语言)