深入理解线程池及其隐患

1.线程池的好处:

  1. 线程的复用,减少线程不断的创建和销毁带来的巨大开销。
  2. 控制线程池的并发数
  3. 线程池可以对线程进行管理

线程池总体的工作过程如下图:
在这里插入图片描述

2.线程池源码:
在 java.util.concurrent 包中,提供了 ThreadPoolExecutor 的实现。
在这里插入图片描述

corePoolSize- 核心池大小,初创建线程池时线程不会立即启动,直到有任务提交才开始启动线程直到线程数目达到corePoolSize。若想一开始就创建所有核心线程需调用prestartAllCoreThreads方法。

maximumPoolSize-池中允许的最大线程数。需要注意的是当核心线程满且阻塞队列也满时才会判断当前线程数是否小于最大线程数,并决定是否创建新线程。

keepAliveTime - 当线程数大于核心时,多于的空闲线程最多存活时间

unit - keepAliveTime 参数的时间单位。

workQueue - 当线程数目超过核心线程数时用于保存任务的队列。主要有3种类型的BlockingQueue可供选择:无界队列,有界队列和同步移交。将在下文中详细阐述。从参数中可以看到,此队列仅保存实现Runnable接口的任务。 别看这个参数位置很靠后,但是真的很重要,因为楼主的坑就因这个参数而起,这些细节有必要仔细了解清楚。

threadFactory - 执行程序创建新线程时使用的工厂。

handler - 阻塞队列已满且线程数达到最大值时所采取的饱和策略。java默认提供了4种饱和策略的实现方式:中止、抛弃、抛弃最旧的、调用者运行。将在下文中详细阐述。

3.可选择的常见阻塞队列BlockingQueue详解:

LinkedBlockingQueue:队列大小无限制,无界队列
ArrayBlockingQueue:有界队列,遵循FIFO原则
SynchronousQueue:同步移交队列,不存储任何元素的队列

4.举例常用的两种线程池及其隐患:
简而言之jdk通过 Executors 封装了几种线程池。
Executors.newCachedThreadPool() 提供了无界线程池,可以进行自动线程回收;Executors.newFixedThreadPool(int) 提供了固定大小线程池,内部使用无界队列;Executors.newSingleThreadExecutor() 提供了单个后台线程。

1.固定大小线程池 Executors.newFixedThreadPool(int)
在这里插入图片描述
看代码一目了然了,线程数量固定,使用无限大的队列LinkedBlockingQueue
若实际场景中有任务不断提交给线程池处理,而里面的线程无法及时的处理任务,终究会导致LinkedBlockingQueue里任务无限增加,从而引发OOM异常

2.缓存(无界 )线程池 Executors.newCachedThreadPool()
在这里插入图片描述
在newCachedThreadPool中如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
由于SynchronousQueue队列是不存储任务的,同时最大线程数为Integer.MAX_VALUE。
所以当实际场景中有任务不断提交给线程池处理,会不断的创建线程去处理任务。若处理任务的速度需要较长时间,线程数不断增加直至引发OOM异常

5.总结:
最近看阿里的 Java开发手册,上面有线程池的一个建议:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

建议后续大伙都使用ThreadPoolExecutor创建线程池,更加深刻的理解线程的运行规则,规避资源耗尽的风险。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值