java 线程池简介
- 2019 年 10 月 25 日
- 笔记
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_32534855/article/details/99608297
简介
什么是“池” : 软件中的“池”,可以理解为计划经济时代的工厂。 首先,作为工厂,你要管理好你生产的东西,老王从你工厂这里拿走了一把斧头,改天他不需要了,还回来,你可以把这把斧头借给老赵; 其次,你又不能无限制的生产,毕竟在资源极度匮乏的时代,如果都被你拿去生产了,其他要用到资源的地方怎么办? 总结成两点,“池”的作用: 复用已有资源 控制资源总量 数据库连接池是这样,线程池也是如此。 你一个任务过来了,我发现池子里有没事干并且还活着的线程,来,拿去用,我也不用费事给你创建一条线程了,要知道线程的创建和销毁可都是麻烦事; 你一个任务过来了,我发现池子的线程都在忙,并且现在池子的线程已经太多了,再不限制下去就要内存溢出了,来,排队去~
Java中常见的4种线程池:
1 FixedThreadPool 2 CachedThreadPool 3 ScheduledThreadPool 4 SingleThreadExecutor
FixedThreadPool: 固定线程数的线程池,可控制线程最大并发数,超出的线程会在队列中等待。

这里的线程数是固定的,每个线程都会从任务队列中去取任务,由于这个取的动作是并发的,所以要求任务队列具有线程安全的特性,所以通常会使用阻塞队列来实现。
CachedThreadPool: 可缓存线程池(特点: 无界线程池,具有自动回收多余线程的功能)

在这种情况下,我们的线程数量并不是固定的,而且我们也没有一个用来存储已提交任务的队列;相反,这种线程池用有一个同步队列(synchronous queue),它只能存储1个Task。每当我们提交了一个任务给线程池,这个任务就会被放到这个队列中。此时,线程池会在已经创建的所有线程中去寻找空闲的线程,并把这一个任务交给这个空闲的线程去执行。如果线程池没能找到一个空闲的线程,说明所有的线程都在工作,那么它就会新建一个线程,并把这个任务交给这个刚刚创建好的线程去执行,这样就达到了动态增加线程数量的目的。
ScheduledThreadPool 支持定时及周期性任务执行的线程池。
这种线程池是专门服务于需要延迟或周期执行任务的场景,例如我们需要每10秒进行一次安全检查。 ScheduledThreadPool主要有3种执行任务的方法: 1. service.schedule 延迟且单次执行,例如10秒后执行,总共只执行一次 2. service.scheduleAtFixedRate 每间隔固定时间执行一次,例如每10秒执行一次 3. service.scheduleAtFixedDelay 在上一个任务执行后,间隔固定时间再次执行。例如:每个Task需要执行5分钟,那么我们可以设定在这个Task直接结束后,等待1分钟再执行下一次的任务,所以最终的效果就是每6分钟该任务会被执行一次。 scheduleAtFixedRate和scheduleAtFixedDelay的区别在于,scheduleAtFixedRate是以任务开始执行的时间为起点,开始计时,而scheduleAtFixedDelay是以任务执行完毕为时间起点进行计时。
这里用来存储Task的queue是一个延迟队列,延迟队列的特点是:不是先进先出,而是会按照延迟时间的长短来排序,下一个即将执行的任务会排到队列的最前面。我们来举个例子:例如我们往这个队列中,放一个延迟10分钟执行的任务,然后再放一个延迟10秒钟执行的任务。通常而言,如果不是延迟队列而是普通队列,那么按照先进先出的排列规则,也就是第一个放置的会排在最前面,也就是延迟10分钟执行的那个任务会放在最前面,但是由于我们此时使用的是延迟队列,延迟队列在排放各个任务的位置的时候,会根据延迟时间的长短来排放。所以。我们第二个放置的延迟10秒钟执行的那个任务,反而会排在延迟10分钟的任务的前面,因为它的执行时间更早。
SingleThreadExecutor 单线程的线程池:它只会用唯一的工作线程来执行任务,它的原理和FixedThreadPool是一样的,但是此时的线程数量被设置为了1。这里的这一个线程,会不停地从任务队列里取任务,然后执行。如果线程在执行任务的时候,遇到了异常导致线程执行中断,那么线程池会重新创建一个线程继续执行后续的任务,而不会永远地中断执行任务。
使用场景:目的是保证所有任务按照提交任务的顺序执行。例如我们想要让第二个任务必须在第一个任务之后执行,同理,或者我们希望让任务三在任务二之后执行,这种场景下就需要用到SingleThreadExecutor。因为执行任务的线程只有一个,所以我们可以保证执行的顺序,而之前那几种线程池在执行的时候,由于有多个线程同时执行,所以我们不能保证各个任务之间的执行顺序。
以上4种线程池的构造函数的参数: 其中fixedThreadPool的keepAliveTime是0秒,这里的0代表永久,也就是fixedThreadPool不会kill任何一个idle的线程,即便该线程无事可做(不执行Task)。 newCachedThreadPool的参数指定了corePoolSize为0,所以CachedThreadPool在最开始的时候并不会创建线程,直到有Task进来,需要线程工作的时候才会创建线程。而可以看出CachedThreadPool的maxPoolSize的值是Integer.MAX_VALUE,这可能会创建数量非常多的线程,甚至导致OOM。 ScheduledThreadPool和SingleThreadPool也是同理。
