ThreadPoolExecutor的使用

2019-08-15 17:22:36 浏览数 (1)

ThreadPoolExecutor也就是线程池。它就是Java为我们开发多线程程序时提供的一个开发框架。它可以统一的管理线程的创建、销毁、优化、监控等,在使用线程池时比我们直接使用原始的线程类更加方便。既然线程池这么方便,那它到底是怎么实现上述的功能呢?下面我们先看一下当用线程池启动一个线程时它的流程图。

线程池的处理流程如下:

当我们用线程池启动一个任务时,线程池首先会检查核心线程池里面的线程数是否已经超过corePoolSize。如果没有超过则创建一个新的线程执行任务。如果超过了,那么将当前执行的任务添加到线程池的工作队列中,但在加入之前会先检查工作队列是否已经满了,如果工作队列已经满了,那么此时它会检查线程池中的线程是否超过了允许的最大数量。如果没有超过则创建线程执行任务,如果超过了最大数量,则按照无法执行的策略处理。

线程池的创建:在创建ThreadPoolExecutor时,会需要传递几个必要的参数,下面我们详细看一下它们每个参数所代表的含义。

  • corePoolSize(初始化的空闲线程):当我们创建ThreadPoolExecutor对象时,可以用corePoolSize参数设置线程池的初始化线程数,也就是空闲线程,当线程池中的线程数量小于corePoolSize时,线程池会重新创建一个新的线程来处理任务,而不是直接使用线程池中的空闲线程。
  • maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。也就最大并发数,也就是说线程池允许多少个线程同时执行。
  • keepAliveTime(线程活动保持时间):当线程池中的线程数大于corePoolSize时,用此参数设置空闲线程等待新任务的时间。在此时间内如果线程没有接收到新的任务,那么当前线程会被销毁。
  • TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
  • BlockingQueue(线程池中的任务队列),当线程池中线程数大于corePoolSize时,新提交的任务会被保存到任务队列中。在线程池中主要有4种不同的任务队列。
  1. ArrayBlockingQueue:是基于数组结构的任务队列。此队列按先进先出的原则对任务进行排序。
  2. LinkedBlockingQueue:是基于链表结构的任务队列。此队列也是按先进先出的原则对任务进行排序。但性能通常要比ArrayBlockingQueue高
  3. SynchronousQueue:一个不存储元素的任务队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
  4. PriorityBlockingQueue:是一个具有优先级的任务队列。此队列中的元素必须能够比较。
  • ThreadFactory():创建线程的线程工厂。
  • RejectedExecutionHandler(饱和策略 ):当线程池中的线程数大于maximumPoolSize时,线程池就不能在处理任何任务了,这时线程池会抛出异常。原因就是这个策略默认情况下是AbortPolicy:表示无法处理新任务时抛出异常。除此之外还有其它几种策略:
  1. AbortPolicy:直接抛出异常。
  2. CallerRunsPolicy:只用调用者所在线程来运行任务。
  3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  4. DiscardPolicy:不处理,丢弃掉。

下面我们用具体的代码来详细说明一下ThreadPoolExecutor的使用。

按照上面的分析,因为我们创建ThreadPoolExecutor对象时初始化的空闲线程是2个,并且我们添加到线程池中的数量也是2个,所以当前任务是由核心线程池执行的任务并不会将任务添加到对列中。如果我在继续向线程池中提交任务,那么因为超过了我们设置的corePoolSize数量,所以此时队列中就有了我们新提交的任务了。

因为我们设置的线程池的最大线程数是3也就是maximumPoolSize的值。如果超过这个值,并且我们没有更改相应的饱和策略,那么此时就会抛出异常信息。

我们发现程序居然没有报错,这是因为什么呢。这是因为参数maximumPoolSize的作是指线程池最大能允许的最大并发数是3也就是说同时可以执行3个线程,但是我们别忘了线程池中还有一个队列呢。队列里存储的就是将要被执行的任务,只是现在已经超过了最大并发数所以队列里的任务只能等待线程池中有其它任务执行完后,它才可以执行。所以此时线程池中允许我们提交任务的最大数就是maximumPoolSize加上队列的数量。但如果我们继续向线程池有添加任务,那么线程池就会报错了,因为已经没有地方存储新任务了,队列也已经满了,所以只能走饱和策略的默认策略就是抛出异常。

下面我们修改一下线程池的饱和策略。

线程池中相关方法的介绍

taskCount:线程池需要执行的任务数。虽然我们向线程池中提交了5个任务,但第5个任务并不是由线程池执行的,是我们修改了饱和策略自己执行的。所以此值返回结果是4。

completedTaskCount:线程池中完成的任务数。

largestPoolSize:线程池中曾经创建过的最大线程数。也就是有多少个线程同是执行,也叫最大并发数。

getPoolSize:线程池中的线程数。如果线程池不销毁,那么线程池里的线程也不会自动销毁。

getActiveCount:活动的线程数。

0 人点赞