ThreadPoolExecutor线程池参数笔记

2019-09-26  本文已影响0人  tenlee

线程池解决了两个问题:

  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);
    ...
}
  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 
                线程池拒绝错误。
  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

上一篇 下一篇

猜你喜欢

热点阅读