线程池学习笔记(2)——ThreadPoolExecutor源码分析

ThreadPoolExecutor构造器参数详解

完整参数的ThreadPoolExecutor构造器如下:

corePoolSize   核心池的大小,这里要注意,线程池创建的时候里面是没有线程的,只有当有任务来的时候才创建线程。只有调用了prestartAllCoreThreads()或者prestartCoreThread()方法才会在线程池创建的时候创建线程,因此线程池创建的时候线程数为0。这里经常会有面试官挖坑问这个问题,请注意。下面代码验证这个问题:

根据上面的结论,运行这个测试用例应该得到的结果:activeCount1 :0,activeCount2 :3

实际输出结果也正如我们说的那样

prestartAllCoreThreads() 初始化所有核心线程,返回已经初始化的线程数
prestartCoreThread() 初始化一个核心线程,返回boolean类型

maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程。
当提交任务数 > 核心数+队列数 时,会创建新的线程,直到达到最大线程数。这个很好验证:

上面的用例说明了前面的结论

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。也就是说,当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

 

workQueue:一个阻塞队列,用来存储等待执行的任务,会对线程池的运行过程产生重大影响。一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
PriorityBlockingQueue;
//上面两种使用较少,我们通常使用下面几种
LinkedBlockingQueue;   ---newSingleThreadExecutor和newFixedThreadPool
SynchronousQueue;      ---newCachedThreadPool
DelayedWorkQueue;         
//线程池的排队策略与BlockingQueue有关

SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

为什么不建议使用Executors的方式创建线程池,Executors方式创建线程池newSingleThreadExecutor和newFixedThreadPool时,队列深度不限,如果任务处理不及时,可能造成队列无限堆积,最终资源耗尽,出现oom。造成系统宕机

所以我们创建线程池一般建议使用ThreadPoolExecutor方式,指定队列深度。这里就有另外一个问题,如果某个时刻突然提交的任务很多,远大于你设置的队列深度,那后来的没能得到线程处理的任务会怎么样?这里就涉及线程池的拒绝处理策略。。。这个地方面试的时候也可能会问到,在实际开发中也很重要,可能因为策略选择不当导致生产问题。这种问题一般很难排查定位,所以开发过程一定要注意

ThreadPoolExecutor提供了4种处理策略:

不同策略应对不同场景,实际生产中,这四种可能并不能满足我们的额需求,这时就需要创建自己的策略,实现RejectedExecutionHandler中的rejectedExecution方法

下面分别验证ThreadPoolExecutor提供的4种策略:

AbortPolicy 超过队列深度时直接抛出异常RejectedExecutionException;

CallerRunsPolicy 如果添加失败,那么主线程会自己调用执行器中的execute方法来执行改任务

DiscardPolicy 如果添加失败,则放弃,并且不会抛出任何异常(导致数据丢失而无从发现

DiscardOldestPolicy 如果添加到线程池失败,会将队列中最早添加的元素移除,再尝试添加,如果失败则按该策略不断重试

结果输出如图

至此,ThreadPoolExecutor提供的4种策略已经测试完毕,结果也已经验证,欢迎大家留言交流沟通。。。

参考博文:https://blog.csdn.net/kaluoye/article/details/81297067

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值