文章目录

  • Executors.newCachedThreadPool()
    • 源码
    • 分析
  • Executors.newFixedThreadPool()
    • 源码
    • 分析
  • 避坑指南
    • 自定义线程池

在一些要求严格的公司,一般都明令禁止是使用Excutor提供的newFixedThreadPool()和newCachedThreadPool()直接创建线程池来操作线程,既然被禁止,那么就会有被禁止的道理,我们先来看一下之所以会被禁止的原因。

Executors.newCachedThreadPool()

源码

    /*** Creates a thread pool that creates new threads as needed, but* will reuse previously constructed threads when they are* available.  These pools will typically improve the performance* of programs that execute many short-lived asynchronous tasks.* Calls to {@code execute} will reuse previously constructed* threads if available. If no existing thread is available, a new* thread will be created and added to the pool. Threads that have* not been used for sixty seconds are terminated and removed from* the cache. Thus, a pool that remains idle for long enough will* not consume any resources. Note that pools with similar* properties but different details (for example, timeout parameters)* may be created using {@link ThreadPoolExecutor} constructors.** @return the newly created thread pool*/public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

大致意思就是,通过该方法会创建一个线程池,当你执行一个任务,并且线程池中不存在可用的已构造好的线程时,它就会创建一个新线程,否则它会优先复用已有的线程,当线程未被使用时,默认 6 秒后被移除。
里面有一句话:
These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.
这些线程池可以很明显的提升那些短期存活的异步任务的执行效率
很明显,官方标注他适合处理业务简单、耗时短的任务,这是为什么呢?
我们接着看 ThreadPoolExecutor 构造方法的描述:

    /*** Creates a new {@code ThreadPoolExecutor} with the given initial* parameters and default thread factory and rejected execution handler.* It may be more convenient to use one of the {@link Executors} factory* methods instead of this general purpose constructor.* 核心线程数* @param corePoolSize the number of threads to keep in the pool, even*        if they are idle, unless {@code allowCoreThreadTimeOut} is set* 最大线程数* @param maximumPoolSize the maximum number of threads to allow in the*        pool* 存活时长* @param keepAliveTime when the number of threads is greater than*        the core, this is the maximum time that excess idle threads*        will wait for new tasks before terminating.* 时间单位* @param unit the time unit for the {@code keepAliveTime} argument* 任务队列* @param workQueue the queue to use for holding tasks before they are*        executed.  This queue will hold only the {@code Runnable}*        tasks submitted by the {@code execute} method.* @throws IllegalArgumentException if one of the following holds:<br>*         {@code corePoolSize < 0}<br>*         {@code keepAliveTime < 0}<br>*         {@code maximumPoolSize <= 0}<br>*         {@code maximumPoolSize < corePoolSize}* @throws NullPointerException if {@code workQueue} is null*/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}

分析

结合ThreadPoolExecutor 构造方法的描述,我们可以知道,当我们调用newCachedThreadPool()方法的时候,它会创建一个核心线程数为 0 ,最大线程数为Integer上限,无用线程存活时间为 6 秒的线程池。
这意味着当我们需要在多线程中执行复杂业务时,它会疯狂的创建线程,因为其他线程中的业务并未执行完。
例如下列代码:

        ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();for (int i = 0; i < 100000000; i++) {threadPool.execute(() -> {//DO SOMETHINGtry {TimeUnit.HOURS.sleep(1);} catch (InterruptedException e) {}});}threadPool.shutdown();threadPool.awaitTermination(1, TimeUnit.HOURS);

模拟瞬间创建100000000十万个任务,且每个任务需要等待一秒钟,会发现电脑内存使用率迅速增加并一直持续到 OOM。

Executors.newFixedThreadPool()

我们再来看一下源码

源码


/*** Creates a thread pool that reuses a fixed number of threads* operating off a shared unbounded queue.  At any point, at most* {@code nThreads} threads will be active processing tasks.* If additional tasks are submitted when all threads are active,* they will wait in the queue until a thread is available.* If any thread terminates due to a failure during execution* prior to shutdown, a new one will take its place if needed to* execute subsequent tasks.  The threads in the pool will exist* until it is explicitly {@link ExecutorService#shutdown shutdown}.** @param nThreads the number of threads in the pool* @return the newly created thread pool* @throws IllegalArgumentException if {@code nThreads <= 0}*/
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

大致描述是:创建一个由固定数量线程并在共享无界队列上运行的线程池,在任何时候都最多只有nThreads个线程存在并执行任务。
如果在任务提交时,所有线程都在工作中,则会将该任务放入到队列中等待,直到有可用的线程。如果某个线程在执行过程中出现异常,那么这个线程会终止,并且会有一个新的线程代替它进行后续的工作,线程池中的线程会一直存在直到线程池被明确的停止掉。

//停止接收新的任务,并继续完成正在执行的任务和队列中的任务
ExecutorService#shutdown//等所有已提交的任务(包括正在跑的和队列中等待的)执行完
//或者等超时时间到
//或者线程被中断,抛出InterruptedException
ExecutorService#awaitTermination(1, TimeUnit.HOURS);//停止接受新的任务,忽略队列中的任务并尝试终止正在执行的任务
ExecutorService#shutdownNow

分析

通过源码我们可以看出,该方法创建一个固定核心线程数和线程池大小的线程池,并且核心数等于最大线程数。看起来好像没有类似newCachedThreadPool无限创建线程的情况,但是在他的描述中有一点很引人注意,
operating off a shared unbounded queue
操作一个共享无界的队列
通过查看newFixedThreadPool()在创建线程池时传入的队列 new LinkedBlockingQueue()

    public LinkedBlockingQueue() {this(Integer.MAX_VALUE);}

会发现,这个队列的最大长度时Integer.MAX_VALUE,这就意味着,未能及时执行的任务都将添加到这个队列里面
随着任务的增加,这个队列所占用的内存将越来越多。最终导致OOM也是迟早的事情。

避坑指南

对于线程池这种东西,其实让我们自己去控制是最好的,我们可以通过实现自定义的线程池提供线程,不仅可以定制化的获取线程执行过程中的状态等信息,还能根据不同的任务使用不同的线程池。
例如,一条简单的 查询操作 和 文件读取操作 就应该放在不同的线程池里面
因为如果两种任务在同一个线程池里面,文件操作本身就是耗时的,它占用了线程之后会导致查询操作等待或者直接被丢弃(取决于自定义线程池任务添加时的规则),这样严重影响了查询性能。

自定义线程池

          //队列长度为100BlockingQueue<Runnable> blockqueue = new LinkedBlockingQueue<Runnable>(100) {/*** 这里重写offer方法* 在接收到新的任务时,会先加入到队列中,当队列满了之后,才会创建新的线程 直到达到线程池的最大线程数* 我们现在需要接收到新任务时,优先将线程数扩容到最大数,后续任务再放入到队列中* 加入队列会调用 offer方法 ,我们直接返回false,制造队列已满的假象*/@Overridepublic boolean offer(Runnable e) {return false;}};ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10,10, TimeUnit.SECONDS,blockqueue , new ThreadFactoryBuilder().setNameFormat("mypool-%d").get(), (r, executor) -> {/*** 这里拒绝策略,被拒绝的任务会走该方法 及没添加到队列中,且没有获取到线程的任务* 因为我们设置的队列中 offer方法固定返回false*/try {//如果允许该任务执行但是不阻塞,及如果进不了队列就放弃,我们可以调用 offer 的另一个多参的方法if (!executor.getQueue().offer(r, 0, TimeUnit.SECONDS)) {throw new RejectedExecutionException("ThreadPool queue full, failed to offer " + r.toString());}//如果我们需要让任务一定要执行,及足协而等待进入队列,可以使用putexecutor.getQueue().put(r)} catch (InterruptedException e) {Thread.currentThread().interrupt();}});

Java — 慎用Executors类中newFixedThreadPool()和newCachedThreadPool()相关推荐

  1. Java基础加强重温_08:线程不安全、线程同步、线程状态、线程状态切换、线程池(Executors类、newFixedThreadPool)、死锁、Lambda表达式、Stream

    摘要 Java基础加强重温_08: 线程安全(线程安全概念.线程不安全案例). 线程同步(同步代码块.同步方法.Lock锁,锁对象). 线程状态(new新建.Runnable可运行.Blocked锁阻 ...

  2. Java:main()函数调用类中方法的限制

    1.在本类中调用自己的方法,方法必须为静态的,否则报错: 通过递归方法,实现N!的算法. public class TestRecursion {public static void main(Str ...

  3. java怎么给类中的私有变量赋值_Java学习笔记分享 如何理解接口抽象类和关键字...

    不知不觉中,千锋重庆学习Java已经半个月了,同学们感觉受益匪浅.有一个同学本来是软件编程专业基础却是意外的差,什么标识符.变量的命名规则.方法的定义.数组.面向对象的封装.继承.多态,还有接口.抽象 ...

  4. java怎么给类中的私有变量赋值_Java核心技术笔记分享------第二章 类与对象

    对象与类 一.面向对象思想的概述 1>面向对象与面向过程: 二者都是一种思想,面向对象是相对于面向过程而言的.面向过程强调的是功能行为.面向对象,将功能封装进对象,强调具备了功能的对象. 面向对 ...

  5. 为什么要在Java的Serializable类中使用SerialVersionUID

    序列化和SerialVersionUID始终是许多Java开发人员的难题. 我经常会看到类似此SerialVersionUID的问题,或者如果不在我的Serializable类中声明SerialVer ...

  6. java time sleep_TimeUnit类中的sleep() 和Thread.sleep()

    TimeUnit是什么? TimeUnit是java.util.concurrent包下面的一个类,TimeUnit提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep(),在很长一段 ...

  7. java可以在类中直接定义语句_基于javac实现的编译时注解

    很多同学都知道jdk中有一个很重要的jar : tools.jar,但是 很少有人知道这个包里面究竟有哪些好玩的东西. javac入口及编译过程 在使用javac命令去编译源文件时,实际上是去执行co ...

  8. java 静态类的实现_关于java:在类中实现静态方法

    通过一本书,我正在经历: "设计一个类名MyInteger.该类包含: ...等等等等等等... 如果此对象中的值分别为偶数,奇数或素数,则方法isEven(),isOdd()和isPrim ...

  9. Java Date日期类中的getMonth()与getYear()提示过时警告

    Date日期类中的getMonth()与getYear()提示过时警告,这里的警告是方法过时,不建议使用. 解决方法,使用Calendar类,但是这里的获取月份是,会比数据中的月份少1,这里需要进行加 ...

最新文章

  1. c#图片上绘制半透明矩形
  2. css3-3 css3背景样式
  3. python 水位_Leetcode 42. 接雨水 - python - 递归 查找分水岭
  4. mysql repos_mysql yum源安装
  5. APP发布Xcode7
  6. C语言单链表定义及各类操作
  7. c语言编写程序统计某给定ascii文件中个字母的出现频率,2016年浙江理工大学理学院C语言程序设计考研复试题库...
  8. selenium控制浏览器
  9. 使用CMD实现批量重命名[转]
  10. Python查看文章中每个单词的出现频率
  11. antd 自定义表单验证 onBlur
  12. 达梦数据库DM8安装配置和使用
  13. 胃不好吃什么养胃 三九胃泰提示常吃芝麻更护胃
  14. win10彻底关闭更新
  15. access四舍五入取整round_access把浮点字段取整是什么函数
  16. [黑科技] WPS通过VB宏函数实现自编号功能
  17. java的jre和jdk
  18. MYSQL数据库实验(用户与权限管理)
  19. signature=b5d482f98802c3bc661cc82639edaa27,Taro
  20. 磊科路由器信号按键_磊科无线路由操作手册

热门文章

  1. 王者服务器什么时候维护完,王者荣耀s9赛季什么时间更新完毕 停机更新后开服时间是几点...
  2. 分子模拟的理论与实践_活动回顾 | 信息学院模拟党支部实践成果汇报会
  3. UE4.27 基于composure的虚拟制片
  4. 计算机思维 Computational Thinking(转载)
  5. 6-5 奇数值结点链表 (20分)
  6. 计算机窗口关闭不了怎么办,电脑上一直出现这个窗口关都关不掉怎么处理
  7. live2d_Live2D解锁丨SR羁绊复刻,生日定制服装上架!
  8. 中文文案排版风格指南
  9. hive查看一张表的分区字段_Hive表分区与索引
  10. CnSeu社工库免费查询_ip代理-golang测试纯真ip库与免费版ipip.net库比较