您的位置:首页 > 其它

线程池的启动方式,以及线程池中的阻塞队列

2020-01-13 14:38 113 查看

1. 线程池的启动方式

newFixedThreadPool()方法:该方法返回一个固定线程池数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲的线程,则立即执行。若没有,则新的任务会被暂存到一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

newSingleThreadExecutor()方法:该方法返回一个只有一个线程的线程池。若多余一个任务被提交到线程池,任务会被保存到一个任务队列里中,待线程空闲,按先入先出的顺序执行队列中的任务。

newCachedThreadPool()方法:该方法返回一个可根据实际情况调整线程数量的线程池。线程池的数量不确定,但若有空闲可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完成后,将返回线程池进行复用。

newSingleThreadSchduledExecutor() 方法:该方法返回一个ScheduledExecutorService对象,线程池的大小为1,ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。

newScheduledThreadPool()方法:该方法返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量。

2. 可选择的阻塞队列BlockingQueue详解

在重复一下新任务进入时线程池的执行策略: 
如果运行的线程少于corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存入queue中,而是直接运行) 
如果运行的线程大于等于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。 
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 
主要有3种类型的BlockingQueue:

无界队列

队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而楼主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。

有界队列

常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。 
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

在我们的修复方案中,选择的就是这个类型的队列,虽然会有部分任务被丢失,但是我们线上是排序日志搜集任务,所以对部分对丢失是可以容忍的。

同步移交队列

如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
BruceMen 发布了6 篇原创文章 · 获赞 0 · 访问量 546 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: