线程池本质上是一种池化技术,而池化技术是一种资源复用的思想,比较常见的有连接池,内存池,对象池。而线程池里面复用的是线程资源,它的核心设计目标,
一是减少线程的频繁创建和销毁带来的新能开销,因为线程创建会涉及到CPU上下文切换、内存分配等工作。
二是线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用。
其次,说一下线程池里面的线程复用技术。因为线程本身并不是一个受控的技术,也就是线程的生命周期是由任务运行的状态决定的,无法人为控制。
所以为了实现线程复用,线程池里面用到了阻塞队列,简单来说就是线程池里面的工作线程处于一直运行状态,它会从阻塞队列去获取待执行的任务,一旦队列空了,那这个工作线程就会被阻塞,直到下次有新的任务进来。
也就是说,工作线程是根据任务的情况实现阻塞和唤醒,从而达到线程复用的目的。
最后,线程池里面的资源限制,是通过几个关键参数来控制的,分别是核心线程数,最大线程数。核心线程数表示默认长期存在的工作线程,而最大线程数是根据任务的情况动态创建的线程,主要是提供阻塞队列中任务的处理效率。
线程池的复用
线程池里面采用了生产者消费者的模式来实现线程复用。
生产者消费者模型,其实就是通过一个中间容器来解耦生产者和消费者的任务处理过程。生产者不断生产任务保存到容器,消费者不断从容器中消费任务。
在线程池里面,因为需要保证工作线程的重复使用,并且这些线程应该是有任务的时候执行,没任务的时候等待并释放CPU资源。
因此它使用了阻塞队列来实现这样一个需求。提交任务到线程池里面的线程称为生产者线程,它不断地往线程池里面传递任务。这些任务会保存到线程池的阻塞队列里面。然后线程池里面的工作线程不断从阻塞队列获取任务去执行。
基于阻塞队列的特性,使得阻塞队列中没有任务的时候,这些工作线程就会阻塞等待。直到又有新的任务进来,这些工作线程再次被唤醒。从而达到线程复用的目的。
线程池的回收
首先,线程池里面分为核心线程和非核心线程。核心线程是常驻在线程池里面的工作线程,它有两种方式初始化。
第一种是向线程池里面添加任务的时候被动初始化,第二种是主动调用prestartAllCoreThreads()方法。
当线程池里面的队列满了的情况下,为了增加线程池的任务处理能力。线程池会增加非核心线程。核心线程和非核心线程的数量,是在构造线程池的时候设置的,也可以动态进行更改。
由于非核心线程是为了解决任务过多的时候临时增加的,所以当任务处理完成后,工作线程处于空闲状态的时候,就需要回收。
因为所有工作线程都是从阻塞队列中去获取要执行的任务,所以只要在一定时间内,阻塞队列没有任何可以处理的任务,那这个线程就可以结束了。
这个功能是通过阻塞队列里面的poll()方法来完成的。这个方法提供了超时时间和超时时间单位这两个参数。当超过指定时间没有获取到任务的时候,poll方法返回null,从而终止当前线程完成线程回收。
默认情况下,线程池只会回收非核心线程,如果希望核心线程也要回收,可以设置allowCoreThreadTimeOut这个属性为true。
一般情况下我们不会去回收核心线程,因为线程池本身就是实现线程复用,而且这些核心线程在没有任务要处理的时候是处于阻塞状态并没有占用CPU资源。