接着上节的内容
,接下来我们就具体的来使用一下这个线程池
,通过这个线程池的具体使用案例
,让大家对线程池的基本使用方法有一个初步掌握
,那么在具体我们写代码之前
,我们首先了解一下
,我们怎么样去创建线程池
,线程池
,我们有两种方式可以创建线程池
,一种方式是我们常用的
,用ThreadPoolExecutor这个对象
,我们实例这个类的对象来创建线程池
,这是可以的
,也是我们推荐使用的
,另外还有一个静态的工具类
,这个Executors里边有一些静态的工具方法
,可以用来创建线程池
,但是
,我们并不推荐使用它
,原因在于我们阿里巴巴的
,这个Java开发手册当中有过这样的一些提及
,有过这样的一段话
,什么话
,在阿里巴巴的开发手册里边强制性的要求
,员工不能够用Executor去创建线程池
,而只能够通过ThreadPoolExecutor的方式来创建线程池
,原因是这样子的处理方式
,能够让同学更加的明确线程池的运行规则
,避免资源耗尽的风险
,而使用这个Executor这个工具类来创建的线程池
,它里边有如下的一些弊端
,我们创建
,它里面创建FixedThreadPool或者是SingleThreadPool
,这是固定线程数的这个线程池和这个单一的线程池
,它里边对于队列的长度
,默认是使用的是等数的最大值
,这样子可能会导致大量的请求的堆积
,从而导致OOM
,也就说内存溢出异常
,
这是这两个线程池类型它里边的弊端
,那么还有两种类型
,CachedThreadPool和ScheduledThreadPool
,这两种支持缓存的线程池
,和能够定时的延迟执行的线程池
,它里边允许创建线程数量也为整数的最大值
,所以说这样子可能会导致大量的线程被创建
,从而导致内存溢出异常
,正是因为这些弊端
,所以说阿里巴巴的开发手册当中
,禁止你去使用Executors创建
,我们推荐使用ThreadPoolExecutor的方式创建
,所以说在这里边我们就先来看一下
,怎么样通过ThreadPoolExecutor对象来创建线程池
,当然我们后边对于Executors的用法
,也会给大家举个例子
,给大家看一下
,了解一下
,但我们在项目开发当中不推荐使用Executors
,那么在这里边
,怎么样使用ThreadPoolExecutor去创建线程池
,它实际上是个类
,在前面我们线程池的宏观的这个结构当中看到了这个类
,所以说对于创建
,通过它来创建线程池
,就是实例化的对象
,实例化的对象
,你就要用到它的构造器
,那么这个类里边我们可以看它的源码
,可以看到它的构造器
,来 我们写一下
,我们的这个什么
,这个ThreadPoolExecutor
,我们看一下它的源码
,来 我们在这里边右键
,我们看一下它的结构
,那么对于它的构造器我们显示出来
,你可以看到它里边实质上有几个构造器
,1 2 3 4个构造器
,有4个构造器
,
我们在这里边
,我们挑选参数最多的这个构造器
,来给这里边传输的参数的含义
,给大家解释清楚
,每一个参数是什么用
,那么
,解释清楚了
,我们才能够去使用这个构造器来创建对应的对象
,所以说在这里边
,当然我们在用的时候是根据我们的这个需求
,你可以选择参数最少的
,你根据我们的需求去选择我们的这个
,使用哪个构造器
,但这里边最多的每一个构造器什么含义
,我们肯定是要事先心里边做到有数的
,好吧
,那么这里边我们就具体来说一下这里边参数
,这里边参数
,我们先简单的说一下
,然后对重点参数我们再详细的去讲解一下
,那么简单来先过一下
,这里边7个参数
,第1个参数叫corePoolSize
,那么它的意思是核心线程池的大小
,那么这里边是一个整数
,那么这里边是我们新的任务到线程以后
,我们线程池会创建新的这个线程
,哪怕是我们线程池里边存在有空闲的线程
,但是我们整个线程池里边
,线程数量还没有达到你这边设置的corePoolSize那么多的时候
,那么我们的线程池都会直接新创建新的线程
,直到我们这个线程数达到这个值的时候为止
,这是第1个参数
,corePoolSize
,它的这个简单的说明它的这个意义
,第2个参数叫做maximumPoolSize
,最大的线程大小
,顾名思义就是我这个线程里边
,能够创建的线程的最大数目
,那么这里边1 2两个参数
,
什么关系
,我们后面再说
,第3个参数是这个keepAliveTime
,就是我们线程池当中工作线程空闲了以后
,那么这个线程还能够保持存活的时间
,第4个参数就是设定你第3个参数它对应的时间单位
,你这边设置第3个参数
,比如说设置10
,那么这是10秒钟
,十毫秒
,10分钟
,10个小时
,取决于我的第4个参数TimeUnit时间单位的设定
,那么第5一个是BlockingQueue
,BlockingQueue它是阻塞队列
,那么这个里边主要是用来存储等待着我执行任务的队列
,由等待的线程池给它分配线程
,从而去执行的这些任务而构成的一个队列
,那么这是第5个
,第6个是线程工厂
,线程工厂
,顾名思义是我们线程池内部
,我们产生线程的工厂工具
,就是说我们线程池里面线程就是由线程工厂它来创建
,第7个叫RejectedExecutionHandler
,那么这个参数主要是
,当我们的队列和线程池都满了的时候
,那么在新的任务过来的时候
,我们这个线程只对这个已经超载的这个新的这个任务
,过来的任务
,那么采取什么样的拒绝策略
,是由这个第7个参数来决定
,所以这里边对于我们这个ThreadPoolExecutor
,它的构造方法当中
,这个参数
,可以支持允许的参数
,最多有这7种
,那么这7种大致的含义
,把它搞清楚
,那么搞清楚它以后
,来进一步我们第1个参数
,
第2个参数有啥子差异
,有啥区别
,一个是核心线程池大小
,一个是最大线程池大小
,那么它们到底有什么区别
,有什么差异
,有什么联系
,来
,我们重点重要参数说明
,首先就来说明它们两个
,默认情况下
,我们的线程池当中线程的初始数量是0
,默认情况下线程池中的初始的线程数是0
,没有
,当我们新的任务来到了线程池的时候
,那么这个时候它才会创建一个新的线程
,那么当我们新创建的线程的数目
,达到了corePoolSize数量的时候
,那么这个时候再来的新的任务
,就会被缓存到我的工作队列里边
,如果说有不断的新的任务到来
,我的这个工作队列也被塞满了的情况下
,这个时候线程池就会再
,又一次的重新的新建线程
,那么新建线程数
,直到达到了我的maximumPoolSize
,这个总的最大线程数的时候
,那么这个时候如果还来任务的话
,那么我们就会根据第7个参数
,RejectedExecutionHandler
,你制定的拒绝策略
,来拒绝掉这个后面的新来的任务
,所以说这里边corePoolSize跟maximumPoolSize
,它们之间的关联是这样子的
,也就说corePoolSize
,核心的线程数是最开始的时候
,一个相对我们线程池的处理任务量相对比较轻松的时候
,它保有的一个线程的一个数量
,当我的这个数量到了
,然后新的任务又来的情况下
,
会把它放在我的工作队列里边等
,当工作队列用满了以后
,这个时候它会增加线程
,增加的线程直到最大maximumPoolSize
,那么这个时候再来任务
,就不会再
,任何情况下都不会再新建我的这个线程
,那么再来的任务会被线程池拒绝
,所以说这两个参数的这个关联
,在这里边一定要把它搞清楚
,那么
,我们的这个下面这个重要的这个参数是这个队列
,就是我们的工作队列
,那么工作队列它是个阻塞队列
,所谓阻塞队列就是前面的任务没有完成
,那么你的这个队列就会就阻塞在那
,如果队列满了
,那么这个新来的这个任务就进不了这队列
,它会阻塞 会等
,所以说用来存储等待执行的任务
,那么通常这时候这是个接口
,BlockingQueue是个接口
,那么它的实现类有好几个
,那么通常我们用的实现类是这几个
,通常使用的是这几个
,我们简单来看一下
,第1个是ArrayBlockingQueue
,这是我们用的最多的应该
,是数组阻塞队列
,它里边这个队列的底层
,是基于数组的数据结构来构建的这个队列
,那么数组结构的有界的阻塞队列
,这个队列实施的是先进先出
,也就是说队列相当于一根管子
,我这边先进来的任务先出去
,先进先出
,那么进来是等
,那么出去就是这个队列被放到我的这个线程里边去执行
,所以说这边它是先进先出的这种元素原则
,排序的这种原则
,第2个是LinkedBlockingQueue
,那么这个是基于链表结构
,因为链表结构的阻塞队列
,
它仍然是先进先出的这个原则
,但是相对于ArrayBlockingQueue的话
,那么链表结构的这个队列
,它要比这个ArrayBlockingQueue的吞吐量要高
,是这样子
,那么我们后面给大家要讲的这个
,静态工厂方法Executor里边的这个newFixedThreadPool
,这个方法创建的这个线程池
,