ThreadPoolExecutor线程池参数笔记

线程池解决了两个问题:

  1. 由于减少了每个任务的调用开销,它们通常在执行大量异步任务时提供更高的性能
  2. 它们提供了一种约束和管理执行大量任务时消耗的资源(包括线程)的方法。 每个 ThreadPoolExecutor还维护一些基本的统计数据,比如已完成任务的数量。

ThreadPoolExecutor类的构造函数如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}
  • corePoolSize
    核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中线程数小于corePoolSize时,即使有空闲线程,仍然会创建新线程执行;当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  • maximumPoolSize
    线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
  • keepAliveTime
    表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut()方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,只要 keepAliveTime非零
  • workQueue:
    任何 BlockingQueue都可用于传输和保存提交的任务。 此队列的使用与线程池大小互相影响:
    • 如果运行的线程少于 corePoolSizeExecutor 总是倾向于添加一个新线程,而不是排队。
    • 如果 corePoolSize 或更多线程正在运行,Executor 总是倾向于将请求排队,而不是添加新线程。
    • 如果请求不能排队,则创建一个新线程,如果线程数超过 maximumPoolSize,任务将被拒绝。
      一般有三种排队策略:
  1. Direct handoffs(直接交接)。 一个很好的默认选择是 SynchronousQueue,它将任务移交给线程,而不以其他方式保留任务。 在这里,如果没有线程可以立即运行任务,将构建一个新线程。 在处理可能具有内部依赖性的请求集时,此策略避免锁定。 直接切换通常需要无限制的 maximumPoolSizes,以避免拒绝新提交的任务。 所以,当命令继续以比处理它们更快的速度到达时,可能造成无限制的线程增长。
  2. Unbounded queues(无界队列)。 使用Unbounded queues(例如没有预定义容量的 LinkedBlockingQueue)将导致在所有corePoolSize 线程忙时在队列中等待新任务。 因此,创建的 corePoolSize 线程不会超过个。 (因此 maximumPoolSize 的值没有任何影响。) 当每个任务完全独立于其他任务时,这可能是适当的,因此任务不会影响彼此的执行; 例如,在网页服务器中。 虽然这种排队方式可以有效地消除请求的短暂爆发,但它也承认,当命令持续以平均快于处理速度到达时,工作队列可能会出现无限增长
  3. Bounded queues有界队列。 有界队列(例如,ArrayBlockingQueue)在使用有限的 maximumPoolSizes 时有助于防止资源耗尽,但是调优和控制可能更加困难。 队列大小和最大池大小可能相互抵消: 使用大队列和小池可以最大限度地减少 CPU 使用、操作系统资源和上下文切换开销,但可能导致人为的低吞吐量。 如果任务经常阻塞(例如,如果它们是 i / o 绑定的) ,系统可能会为更多的线程安排时间。 使用小队列通常需要更大的池大小,这使 cpu 更加繁忙,但可能会遇到不可接受的调度开销,这也降低了吞吐量
    总结:
if (wc + 1< coreSize`)// 如果线程正在运行的线程数 wc+1 小于 coreSize,则新建一个线程。
else //如果大于 coreSize 
         if (wc + 1 < workQueue.size()) 如果队列没满,有限加入队列。
         else 
               if (wc + 1  - workQueue.size() > maximumPoolSize) 
                如果需要的线程数没有超过最大线程数,则创建线程
               else 
                线程池拒绝错误。
  • ThreadFactory:
    线程工厂,用来创建线程
  • RejectedExecutionHandler:
    Executor关闭执行execute(Runnable)中提交的新任务被拒绝时,以及 Executor 为最大线程和工作队列容量使用有限的边界时,并且已经饱和时被拒绝。 在这两种情况下,execute 方法都会调用其 RejectedExecutionHandler.rejectedExecution (Runnable,ThreadPoolExecutor)。 提供了四个预定义的处理程序策略:
  1. 默认的ThreadPoolExecutor.AbortPolicy,处理程序在拒绝时抛出运行时异常RejectedExecutionException
  2. ThreadPoolExecutor.CallerRunsPolicy由调用线程本身运行这个任务。 这提供了一个简单的反馈控制机制,可以降低新任务提交的速度。
  3. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  4. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

如何设置参数

如何来设置
需要根据几个值来决定
tasks :每秒的任务数,假设为500~1000
taskcost:每个任务花费时间,假设为0.1s
responsetime:系统允许容忍的最大响应时间,假设为1s
做几个计算
corePoolSize = 每秒需要多少个线程处理?
threadcount = tasks/(1/taskcost) =taskstaskcout = (500~1000)0.1 = 50~100 个线程。corePoolSize设置应该大于50
根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可
queueCapacity = (coreSizePool/taskcost)responsetime
计算可得 queueCapacity = 80/0.1
1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行
切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。
maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
计算可得 maxPoolSize = (1000-80)/10 = 92
(最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数
rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理
keepAliveTime和allowCoreThreadTimeout采用默认通常能满足

参考:https://www.cnblogs.com/waytobestcoder/p/5323130.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容