创建线程池需要注意的事情


1 介绍

阿里的java代码规范有一条是强制建议开发者不要使用Executors创建线程池。
在这里插入图片描述
而是建议我们使用原生的ThreadPoolExecutor进行创建,既然这么建议了,自然是有它建议的道理,下面我们就来简单的分析一下为什么不推荐我们使用Executors进行创建。

2 分析

作为一个java开发,线程池是我们工作当中,必会去使用的一个工具,关于线程池的好处,自然是不用多说,反正就是好处很多。

既然是阿里推荐我们使用ThreadPoolExecutor(这个类也就是线程池的核心类) 进行创建线程池,那我们就先看一下这个类的核心方法:
在这里插入图片描述
ThreadPoolExecutor 类所提供的几个开放的公共方法就是上图的1,2,3 部分,关于第二部分的方法,主要就是对于已经创建的线程池对象进行功能增强,比如设置核心线程是否可以超时和终止的策略,关于第三部分的公共方法就是获取对应线程池的一些基础参数

我们重点看一下1部分中的方法,我们会发现,1部分都是关于ThreadPoolExecutor的构造函数,只是对应的参数个数不同而已,我们重点看一下构造函数中参数最多的那个
在这里插入图片描述
其实其他的构造函数只是针对没有的参数进行默认初始化,最终都是会调到这个最长的构造函数进行线程池的创建。

corePoolSize:表示线程池中常驻的核心线程数,等于0表示任务执行完毕之后,没有任何任务请求后销毁线程池的线程,如果大于0即使本地任务执行完毕,核心线程池也不会销毁。
maximumPoolSize:表示线程池能够容纳的最大线程数,当maximumPoolSize == corePoolSize 表示固定大小的线程池
keepAliveTime:这个参数是针对于当空闲的时间达到keepAliveTime时,大于corePoolSize 那一部分的线程会被销毁
unit: 针对参数keepAliveTime的时间单元
workQueue:缓存队列,用于存放当任务无法被核心线程执行时,会存放到缓存队列中
threadFactory:线程工厂,可以使用该方法创建对应的线程
handler:拒绝策略:当缓存队列无法存放新的任务时,并且当前线程池的最大线程数达到maximumPoolSize时,会进行策略淘汰,

RejectedExecutionHandler:java线程池中主要有以下几个拒绝策略,当然你也可以自定义淘汰策略:

  • AbortPolicy:该策略是线程池的默认淘汰策略,使用该策略时,如果线程池队列满了,丢弃这个任务并抛出RejectExecutionException异常
  • DiscardPolicy:如果线程池满了,会直接丢弃这个任务并不会抛出异常
  • DiscardOldestPolicy:丢弃最老的,也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入
  • CallerRunsPolicy:如果添加到线程池失败,那么主线程自己会执行该任务,不会等待线程池中的线程去执行

线程池的工作流程:

  • 如果正在运行的线程数小于coreSize,马上创建线程执行任务,不排队等待,
  • 如果正在运行的线程数>=coreSize,把该task放入队列
  • 如果队列已满 && 正在运行的线程数 < maximumPoolSize,创建新的线程执行该task;
  • 如果队列已满 && 正在运行的线程数 >= maximumPoolSize,线程池调用handler的reject方法拒绝本次提交。
  • 如果一个提交一个任务,当线程池中线程数<coreSize,即使是其他空闲线程能够执行该任务,也会创建该线程

理清了线程池的核心流程和核心代码之后,我们再看一下Executors提供给我们的一些快捷创建线程的方法
在这里插入图片描述

主要的几个方法是:

public static ExecutorService newCachedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ExecutorService newSingleThreadExecutor()

针对于**newCachedThreadPool()**创建线程的方法
在这里插入图片描述
他的内部实际上是设置核心线程数为0,并且对应的最大线程数为Integer.MAX_VALUE,设置60秒线程空闲销毁策略,并且设置了一个同步队列SynchronousQueue。
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
可能会创建大量的线程,最终会导致OOM

针对newFixedThreadPool(int nThreads) 创建线程池的方法:
在这里插入图片描述
他的内部是设置了一个固定nThreads个数的核心线程池,创建了容量为Integer.MAX_VALUE大小的LinkedBlockingQueue的缓存队列,
可能会导致当任务无法被核心线程执行时,会无限的存储到缓存队列中,并且没有对应的淘汰策略,最终会导致OOM

针对**newScheduledThreadPool(int corePoolSize)**创建线程池的方法:
在这里插入图片描述
内部的实现逻辑和newCachedThreadPool方法差不多,只是他们的核心线程可以被设置一个固定的值,最大的线程数仍然是Integer.MAX_VALUE
也可能会 导致创建大量的线程池,出现OOM的问题

正对newSingleThreadExecutor() 的创建线程池的方法:
在这里插入图片描述
它的内部实现逻辑和newFixedThreadPool基本一致,使用了Integer.MAX_VALUE最大容量的缓存队列,
最终可能导致新加入的任务会一直存储到缓存队列中,没有淘汰策略,导致OOM

发布了55 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zcswl7961/article/details/103634869
今日推荐