特此感谢,低并发编程老师的公众号里面讲的线程池一节,受益匪浅,才有了自己整理的笔记
一步一步拆解框图,如下:
- 如果我自己来设计这个工具类的话(出题人给定了一个接口:public interface Executor{public void execute(Runnable r)};)。那思路就是,我写一个类实现出题人给的接口,类里面重写接口里面的方法,方法体就是最初的想要异步执行的程序,new Thread®.start();相当于不管是伙伴A,伙伴B…都来调用我这个子类中的重写过的方法就行,不用你们自己去new Thread®.start();
public ExecutorVersion01 implements Executor{
public void execute(Runnable r){
new Thread(r).start();
}
}
- 方便了一点点,但是,要是一亿个人都调这个子类中的方法去想要异步执行,他其实也是创建了一亿个线程呀,这么多线程肯定不合适吧,你一个饭馆会招聘这么多服务员嘛,肯定不会呀,肯定要共用起来好一点呀。好,那先实现一下控制一下线程的数量
- 一个Worker貌似有点少(要是tasks队列满了怎么办,相当于客人来的很多你一个服务员手脚再麻利也忙不过来呀)。好,那先把Worker线程的数量增加一下
- 具体数量让使用者决定。调用时再传入(叫做核心线程数corePoolSize)
至此,设计思路大体完成,打眼一看,还有几个缺陷:
- 具体数量让使用者决定。调用时再传入(叫做核心线程数corePoolSize)
- 假设当提交任务的数量突增时,工作线程Worker和任务队列Tasks都被占满了,就只能走拒绝策略,其实就是被丢弃掉,这还不是赶走客人嘛
我们可以发明一个新属性,叫最大线程数maximumPoolSize.当核心线程数和队列都满了时,新提交的任务仍然可以通过创建新的工作线程(叫做非核心线程),直到工作线程数达到maximumPoolSize为止(这样就可以缓解一时的QPS高峰期了,我也不用无脑去设置过大的核心线程数了)
- 开始时当workCount < corePoolSize时,通过创建新的Worker(此时虽然前几个线程本质上和之前的Worker一样,但是此时叫做核心线程)来执行任务
- 当workCount >= corePoolSize时,停止创建新线程,把任务直接丢到任务队列中
- 又当任务队列一满时,workCount < maximumPoolSize时,不再直接走拒绝策略(满了就丢那种),而是创建非核心线程,直到workCount = maximumPoolSize再满了的话再走拒绝策略
在重新回顾一下,线程池的定义
线程池(程序运行的本质就是(进程和线程)占用系统的资源,所以为了优化资源的使用,才有了池化技术),在池子里创建线程。
接下来就是:三大方法+7大参数+4种拒绝策略
- 三大方法**(Executors这个工具类中的三大方法,但是阿里巴巴开发手册上说不允许用Executors去创建线程池而是通过ThreadPoolExecutor的方式):三大方法的底层都是在方法中return new ThreadPoolExecutor(…)来实现开启了线程池**
框中的三个方法,如下:
调用Executors的三大方法返回或者说创建一个线程池的代码如下:
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Executors.newFixedThreadPool(5);//创建一个有固定数目线程的线程池
Executors.newCachedThreadPool();//创建一个可伸缩的,遇强则强遇弱则弱的线程池
这三个方法都是Executors接口的,咱们再看看这个接口:顶层接口Executors详解(可以把这个类看作是一个工具类,它里面有三大方法可以用来创建线程池,)
有几个注意点:
另外,这个Executors接口中的三大方法底层都是在方法中return new ThreadPoolExecutor(…)来实现开启了线程池
内部有一个this进行调用,咱顺着this往下找
那咱们再继续顺着看看这七个参数:
public ExecutorByHu(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
{
... // 省略一些参数校验
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下来一个一个具体看:
- int corePoolSize:核心线程数(核心线程池大小)
- int maximumPoolSize:最大线程池大小
- long keepAliveTime:非核心线程的空闲时间
- TimeUnit util:空闲时间的单位(超时单位)
- BlockingQueue workQueue:任务队列(线程安全的阻塞队列)
- ThreadFactory threadFactory:线程工厂,用来创建线程的,一般不用动(Executors.defaultThreadFactory();)
- RejectedExecutionHandler handler:拒绝策略(有四种拒绝策略,各有各的拒绝特点)
拒绝策略(有四种拒绝策略,各有各的拒绝特点);相当于银行那种,有五个柜台,然后假设还有一个候客区共有5个座位。然后假如人很多时,柜台已经有五个人在办业务了并且候客区也坐满了五个人,那么如果第十一个来了,就得用拒绝策略把第十一个人拒绝了
- 第一种拒绝策略是:(在线程池这里体现出来就是如果第11个人来了就不处理并抛出异常)
- 队列满了后,第11个人就会被默默抛弃不会被处理(执行),也不会抛出啥异常
下来再看看几个赠送的礼品:
使用线程池时:
ok,拜拜~