线程池ThreadPoolExecutor学习

在阅读vitamin的heartbeat功能时,内部实现使用到了Java中的threadpoolExecutor ,这里对自己学习到的一些知识做一些总结

线程池的目的与使用的背景:在并发请求数量非常多,但是处理的时间又比较短,开销主要在线程的生成和销毁上时,线程池就能很好的派上用场

 

Java中的线程池是用ThreadPoolExecutor类来实现的,以下是主要的类图

 

主要了解线程的创建、管理以及后台任务的调度等方面的执行原理。结合这些 去理解heartbeat中的代码

首先heatbeat中的成员变量:

 

存在于executor相关的两个接口,首先解释这两个接口的含义:

  • Executor:一个运行新任务的简单接口;里面会定义execute方法,方法的入参为Runnable对象;一般用启动thread调用的是thread.start的方法,而execute方法就是替代通常创建或启动线程的方法。对于不同的Executor实现,execute()方法可能是创建一个新线程并立即启动,也有可能是使用已有的工作线程来运行传入的任务,也可能是根据设置线程池的容量或者阻塞队列的容量来决定是否要将传入的线程放入阻塞队列中或者拒绝接收传入的线程。
  • ExecutorService:扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法;增加了shutDown(),shutDownNow(),invokeAll(),invokeAny()和submit()等方法。如果需要支持即时关闭,也就是shutDownNow()方法,则任务需要正确处理中断。
  • ScheduledExecutorService:扩展ExecutorService接口并增加了schedule方法。调用schedule方法可以在指定的延时后执行一个Runnable或者Callable任务。ScheduledExecutorService接口还定义了按照指定时间间隔定期执行任务的scheduleAtFixedRate()方法和scheduleWithFixedDelay()方法。

 

初始化代码中 sendHeartbeatService 和 processHeartbeatService 都用了ThreadPoolExecutor构造方法初始化

 

这里需要理解这个ThreadPoolExecutor构造方法才能理解各个参数的含义:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

  • corePoolSize:核心线程数量,当有新任务在execute()方法提交时,会执行以下判断:

    1. 如果运行的线程少于 corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;
    2. 如果线程池中的线程数量大于等于 corePoolSize 且小于 maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;
    3. 如果设置的corePoolSize 和 maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去从workQueue中取任务并处理;
    4. 如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;

    所以,任务提交时,判断的顺序为 corePoolSize --> workQueue --> maximumPoolSize。

  • maximumPoolSize:最大线程数量;
  • workQueue:等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
  • keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
  • threadFactory:它是ThreadFactory类型的变量,用来创建新线程。默认使用Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
  • handler:它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。线程池提供了4种策略:
    1. AbortPolicy:直接抛出异常,这是默认策略;
    2. CallerRunsPolicy:用调用者所在的线程来执行任务;
    3. DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
    4. DiscardPolicy:直接丢弃任务;

这里面有一个重要的逻辑需要了解:execute 方法执行一个线程时,是有一个调度过程的,会根据corePoolSize 、队列是否阻塞和maximumPoolSize这些参数进行判断,是否创建新线程执行还是到队列里等待

等,下面是流程图

 

 

posted @ 2019-12-02 20:50  脱线的脑子  阅读(152)  评论(0编辑  收藏  举报