【㉿Java并发】说一下线程池内部工作原理(ThreadPoolExecutor)

线程池有五种状态:RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED。

  • RUNNING:接收并处理任务。
  • SHUTDOWN:不接收但处理现有任务。
  • STOP:不接收也不处理任务,同时终端当前处理的任务。
  • TIDYING:所有任务终止,线程池会变为 TIDYING 状态。当线程池变为 TIDYING 状态时,会执行钩子函数 terminated()。
  • TERMINATED:线程池彻底终止的状态。

内部变量ctl 定义为 AtomicInteger ,记录了“线程池中的任务数量”和“线程池的状态”两个信息。共 32 位,其中高 3 位表示”线程池状态”,低 29 位表示”线程池中的任务数量”。

ThreadPoolExecutor构造方法的参数

corePoolSize

线程池中核心线程的数量。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于 corePoolSize。如果调用了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有基本线程。如果当前线程数等于corePoolSize,仍有任务提交,则会保存到阻塞队列中。

maximumPoolSize

线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于 maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。

keepAliveTime

线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于 corePoolSize 时才会生效。

unit

keepAliveTime 的单位。TimeUnit

workQueue

用来保存等待执行的任务的阻塞队列,等待的任务必须实现 Runnable 接口。我们可以选择如下几种:

  • ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。
  • LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
  • SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作,反之亦然。
  • PriorityBlockingQueue:具有优先界别的阻塞队列。

threadFactory

用于设置创建线程的工厂。该对象可以通过 Executors.defaultThreadFactory()。它是通过 newThread() 方法提供创建线程的功能,newThread() 方法创建的线程都是"非守护线程"而且线程优先级都是 "Thread.NORM_PRIORITY"。

handler

RejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。

线程池提供了四种拒绝策略:

  • AbortPolicy:直接抛出异常,默认策略;
  • CallerRunsPolicy:用调用者所在的线程来执行任务;
  • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  • DiscardPolicy:直接丢弃任务;

当然我们也可以实现自己的拒绝策略,例如记录日志等等,实现 RejectedExecutionHandler 接口即可。

线程池执行流程

当添加新的任务到线程池时:

  • 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务
  • 线程数量达到了 corePoolSize,则将任务移入队列等待
  • 队列已满,新建线程(非核心线程)执行任务
  • 队列已满,总线程数又达到了 maximumPoolSize,就会由 handler 的拒绝策略来处理

 

参考:

 

posted @ 2023-03-17 23:02  残城碎梦  阅读(161)  评论(0编辑  收藏  举报