一. 什么是线程池?
线程池就是以一个或多个线程[循环执行]多个应用逻辑的线程集合.
通俗解释就是事先准备好一些资源,有人要用,就来拿,用完还回!
二. 线程池的好处是什么?
1.降低资源的消耗,线程池中的线程是可以重用的,不用多次的创建和销毁,提高了系统的性能
2.提高响应速度
3.线程池中的队列可以管理大量的任务,任务的执行,调度,排队,丢弃等事宜都由线程池来管理,做到任务可控
4.方便管理,线程池对线程进行一些维护和管理,比如线程定时执行,线程生命周期管理,多少个线程并发,线程执行的监控等
线程复用,可以控制最大并发数,管理线
传统的编写线程:
1:新建线程,执行任务,任务完毕,线程销毁。线程的频繁新建/销毁都是由JVM管理的,非常的消耗系统性能。
2:当任务比较小时,花在创建和销毁线程上的时间会比任务执行的时间长。尤其是如果有大量的任务时,线程的大量创建和销毁,有内存溢出的风险。
三. 线程池介绍:
接口:
1.Executor 其内仅有execute(Runnable task);方法。
2.ExecutorService 继承Executor,对线程有更多的管理,常用的有:submit()方法、shutdown()方法等。
3.ScheduledExecutorService 继承ExecutorService,对线程又进一步的支持了定时执行的职能。
类:
1.AbstractExecutorService 默认实现了ExecutorService接口中的部分方法。
2.ThreadPoolExecutor 我们常用的类,里面的职能有:维护任务队列,维护线程组,管理线程调度,执行,监控,等。
3.ScheduledThreadPoolExecutor 里面的职能相对于父类ThreadPoolExecutor来说,多了对线程定时执行的职能。
三大方法:
//创建单个线程
ExecutorService executorService = Executors.newSingleThreadExecutor();
//创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//创建可伸缩的,遇强则强,遇弱则弱
ExecutorService executorService = Executors.newCachedThreadPool();
线程池不允许用Executors去创建,而是通过ThreadPoolExecutor方式创建,理解线程池的参数和运行规则
Executors创建的弊端:
1).FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE(约21亿),可能会堆积大量请求,导致OOM.
2).CachedThreadPool和ScheduledThreadPool:
允许创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,导致OOM.
七大参数:
/**
* 线程池的构造函数
*/
public ThreadPoolExecutor(
//核心线程数 举例说明: 有编制的正式快递员员工个数
int corePoolSize,
//最大线程数 双11了,包裹量急剧增多,正式员工忙不过来,只能新招临时工 =( 最大 - 核心 )
int maximumPoolSize,
//临时工呆多久 临时工就是临时工(包裹量不多的时候会被辞退的),能呆几天
long keepAliveTime,
//临时工呆多久的计量单位 临时工呆多少小时,那么unit就计量单位为小时;临时工能呆多少天,unit计量单位就是天
TimeUnit unit,
//任务队列 需要派送的大量包裹存储的地方
BlockingQueue<Runnable> workQueue,
//线程工厂
ThreadFactory threadFactory,
//异常处理
RejectedExecutionHandler handler)
四种拒绝策略
-
AbortPolicy
默认策略,不执行此任务,而且直接抛出RuntimeException。 切记execute()需要try catch,否则程序会直接退出 -
DiscardPolicy
直接抛弃,任务不执行,空方法 -
DiscardOldestPolicy
从队列里面抛弃head的一个任务,并再次尝试调用execute(task); -
CallerRunsPolicy
当前线程调用的execute(task)方法,当前线程阻塞在这里,直至task执行完毕 -
自定义策略 (常用)自定义类实现RejectedExecutionHandler。例:可以先把任务写入文件或者数据库,以防止任务丢弃
线程池工作原理的流程图:
线程池工作原理描述:
1:有新任务了,尽可能的让核心线程去执行;
2:核心线程都在忙了,再来的任务就放到队列中去排队等待被执行;
3:队列中都塞满了任务,还来新任务,就临时招募非核心线程来执行刚到的新任务;
4:队列满了,核心线程/非核心线程都在忙,还来新任务,启用安全策略;
5:安全策略来处理仍源源不断到来的新任务,安全策略决定是丢弃新来的任务,还是其它处理。
四. 模拟线程池工作流程
1.Core核心线程有三个(1,2,3),已满
2.现加入3个人,没有空闲线程,则先进入等待区(阻塞队列)等待.
3.又来了3个人,没有空闲线程,等待区(阻塞队列也满),则创建新的线程(总线程数<max)来处理
4.现在max最大,阻塞队列也满了,接下来加入的人就会被拒绝!(四种拒绝策略)
- (1) new ThreadPoolExecutor.AbortPolicy() //默认策略
, 银行满了,还有人加入,不处理这个人并抛出异常 RejectExecutionException - (2)new ThreadPoolExecutor.CallerRunPolicy()
//哪来的回哪去 会交给主线程处理 - (3)new ThreadPoolExecutor.DiscardPolicy()
//队列满了,丢掉任务,不抛出异常 - (4)new ThreadPoolExecutor.DiscardOldestPolicy()
//队列满了,会尝试和最早的竞争(若1号处理完了,则加入),不会抛出异常