线程池的工作原理
线程池是为了减少频繁的创建线程和销毁线程带来的性能损耗,线程池的工作原理如下图:
线程池分为核心线程池,线程池的最大容量,还有等待任务的队列,提交一个任务,如果核心线程没有满,就创建一个线程,如果满了,就是会加入等待队列,如果等待队列满了,就会增加线程,如果达到最大线程数量,就会按照一些丢弃的策略进行处理。
线程池的参数有哪些?
- corePoolsize: 线程池核心线程数量。默认情况下,线程池中线程的数量如果<=corePoolSize,那么即使这些线程外于空闲状态,那也不会被销毁。
- maximumPoolsize: 线程池中最多可容纳的线程数量。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程且当前线程池的线程数量小于corePoolsize,就会创建新的线程来执行任务,否则就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolsize,就不会创建新线程,就会去执行拒绝策略。
- keepAliveTime: 当线程池中线程的数量大于corePoolsize,并且某个线程的空闲时间超过了keepAliveTime,那么这个线程就会被销毁。
- unit: 就是keepAliveTime时间的单位。
- workQueue: 工作队列。当没有空闲的线程执行新任务时,该任务就会被放入工作队列中,等待执行。
- threadFactory: 线程工厂。可以用来给线程取名字等等。
- handler: 拒绝策略。当一个新任务交给线程池,如果此时线程池中有空闲的线程,就会直接执行,如果没有空闲的线程,就会将该任务加入到阻塞队列中,如果阻塞队列满了,就会创建一个新线程,从阻塞队列头部取出一个任务来执行,并将新任务加入到阻塞队列末尾。如果当前线程池中线程的数量等于maximumPoolsize,就不会创建新线程,就会去执行拒绝策略。
线程池工作队列满了有哪些拒接策略?
当线程池的任务队列满了之后,线程池会执行指定的拒绝策略来应对,常用的四种拒绝策略包括:CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy,此外,还可以通过实现RejectedExecutionHandler接囗来自定义拒绝策略。
四种预置的拒绝策略:
- CallerunsPolicy,使用线程池的调用者所在的线程去执行被拒绝的任务,除非线程池被停止或者线程池的任务队列已有空缺。
- AbortPolicy,直接抛出一个任务被线程池拒绝的异常
- DiscardPolicy,不做任何处理,静默拒绝提交的任务
- DiscardOldestPolicy,抛弃最老的任务,然后执行该任务, 自定义拒绝策略,通过实现接口可以自定义任务拒绝策略。
线程池的种类
- ScheduledThreadPool: 可以设置定期的执行任务,它支持定时或周期性执行任务,比如每隔 10秒钟执行一次任务,我通过这个实现类设置定期执行任务的策略。
- FixedThreadPool:它的核心线程数和最大线程数是一样的,所以可以把它看作是固定线程数的线程池,它的特点是线程池中的线程数除了初始阶段需要从 0开始增加外,之后的线程数量就是固定的,就算任务数超过线程数,线程池也不会再创建更多的线程来处理任务,而是会把超出线程处理能力的任务放到任务队列中进行等待。而且就算任务队列满了,到了本该继续增加线程数的时候,由于它的最大线程数和核心线程数是一样的,所以也无法再增加新的线程了。
- CachedThreadPool: 可以称作可缓存线程池,它的特点在于线程数是几乎可以无限增加的(实际最大可以达到 integer.MAXVALUE,为 2^31-1,这个数非常大,所以基本不可能达到),而当线程闲置时还可以对线程进行回收。也就是说该线程池的线程数量不是固定不变的,当然它也有一个用于存储提交任务的队列,但这个队列是 SynchronousQueue,队列的容量为0,实际不存储任何任务,它只负责对任务进行中转和传递,所以效率比较高。
- singleThreadExecutor: 它会使用唯一的线程去执行任务,原理和FixedThreadPool是一样的,只不过这里线程只有一个,如果线程在执行任务的过程中发生异常,线程池也会重新创建一个线程来执行后续的任务。这种线程池由于只有一个线程,所以非常适合用于所有任务都需要按被提交的顺序依次执行的场景,而前几种线程池不一定能够保障任务的执行顺序等于被提交的顺序,因为它们是多线程并行执行的。
- singleThreadScheduledExecutor: 它实际和 ScheduledThreadPool线程池非常相似,它只是ScheduledThreadPool 的一个特例,内部只有一个线程。