ThreadPoolExecutor线程池原理及其执行方法(详解)

  

<强>

  

对于线程池大部分人可能会用,也知道为什么用。无非就是任务需要异步执行,再者就是线程需要统一管理起来。对于从线程池中获取线程,大部分人可能只知道,我现在需要一个线程来执行一个任务,那我就把任务丢到线程池里,线程池里有空闲的线程就执行,没有空闲的线程就等待。实际上对于线程池的执行原理远远不止这么简单。

  

在ThreadPoolExecutor,实际上更多的我们可能用到的是执行人:FixedThreadPool, SingleThreadPool, CachedThreadPool,这三个线程池并不是

        公共ThreadPoolExecutor (int, int corePoolSize maximumPoolSize长keepAliveTime TimeUnit unit, BlockingQueue工作队列)      

首先就从这几个参数开始来了解线程池。

  

<强>

  

<强>

  

<强>

  

<强>

  

<强>

  

corePoolSize的线程数量,好像平时用到线程池的时候最多就只需要传递一个线程池大小的参数就能创建一个线程池啊,FixedThreadPool, SingleThreadExecutor, CachedThreadPool,当然如果我们想要自己发挥创建自定义的线程池就得自己来“配置”有关线程池的一些参数。

  

当把一个任务交给线程池来处理的时候,线程池的执行原理如下图所示参考自《

  

  

①,有空闲线程则创建一个线程来执行任务。

  

②当核心线程池里已经没有线程可执行的时候,此时将任务丢到任务队列中去。

  

③如果任务队列(有界)也已经满了的话,但运行的线程数小于最大线程池的数量的时候,此时将会新建一个线程用于执行任务,但如果运行的线程数已经达到最大线程池的数量的时候,此时将无法创建线程执行任务。

  

所以实际上对于线程池不仅是单纯地将任务丢到线程池,线程池中有线程就执行任务,没线程就等待。

  

为巩固一下线程池的原理,现在再来了解上面提到的常用的3

  

<强> Executors.newFixedThreadPool创建一个固定数量线程的线程池。

     //执行器# newFixedThreadPool   公共静态ExecutorService newFixedThreadPool (int nThreads) {   返回新ThreadPoolExecutor (nThreads nThreads 0 l TimeUnit。毫秒,新LinkedBlockingQueue ());   }      

可以看到newFixedThreadPool中调用的是ThreadPoolExecutor类,传递的参数corePoolSize=maximumPoolSize=nThread。回顾线程池的执行原理,当一个任务提交到线程池中,首先判断核心线程池里有没有空闲线程,有则创建线程,没有则将任务放到任务队列(这里是有界阻塞队列LinkedBlockingQueue)中,如果任务队列已经满了的话,对于newFixedThreadPool来说,它的最大线程池数量=核心线程池数量,此时任务队列也满了,将不能扩展创建新的线程来执行任务。

  

<强>执行人。newSingleThreadExecutor:创建只包含一个线程的线程池。

     //执行器# newSingleThreadExecutor   公共静态ExecutorService newSingleThreadExecutor () {   返回新FinalizableDelegateExecutorService(新ThreadPoolExecutor (1 1 0 l TimeUnit。毫秒,新LinkedBlockingQueue ()));   }      

只有一个线程的线程池好像有点奇怪,并且并没有直接将返回ThreadPoolExecutor,甚至也没有直接将线程池数量1传递给newFixedThreadPool返回。那就说明这个只含有一个线程的线程池,或许并没有只包含一个线程那么简单。在其源码注释中这么写到:创建只有一个工作线程的线程池用于操作一个无界队列(如果由于前驱节点的执行被终止结束了,一个新的线程将会继续执行后继节点线程)任务得以继续执行,不同于newFixedThreadPool(1)不会有额外的线程来重新继续执行后继节点。也就是说newSingleThreadExecutor自始至终都只有一个线程在执行,这和newFixedThreadPool一样,但如果线程终止结束过后newSingleThreadExecutor则会重新创建一个新的线程来继续执行任务队列中的线程,而newFixedThreaPool则不会。

  

<强> Executors.newCachedThreadPool:根据需要创建新线程的线程池。

     //执行器# newCachedThreadPool   公共静态ExecutorService newCachedThreadPool () {   返回新ThreadPooExecutor(0,整数。MAX_VALUE 60 l TimeUnit。秒,新SynchronousQueue ());   }      

可以看到newCachedThread返回的是ThreadPoolExecutor,其参数核心线程池corePoolSize=0, maximumPoolSize=Integer.MAX_VALUE,这也就是说当任务被提交到newCachedThread线程池时,将会直接把任务放到SynchronousQueue任务队列中,maximumPool从任务队列中获取任务。注意SynchronousQueue是一个没有容量的队列,也就是说每个入队操作必须等待另一个线程的对应出队操作,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,newCachedThreadPool会不断创建线程,线程多并不是一件好事,严重会耗尽CPU和内存资源。

ThreadPoolExecutor线程池原理及其执行方法(详解)