线程是系统资源,每创建新的线程会占用资源,且创建和销毁对象开销较大;
高并发时,如果为每个任务都创建新的线程,则对内存占用太大,可能会内存溢出;
线程池技术的引入,就是为了解决这一问题.

一个线程集合用来控制线程的数量,会先创建一定数量的线程放入空闲队列中,当任务来了之后就给任务分配线程去执行;
当线程被任务占满后,会将任务放到阻塞队列排队,等待其他线程执行完毕,再从队列中取出任务来执行;
如果选的是有界队列,当阻塞队列满了,再来新的任务,就会创建救急线程;
当救急线程也满了,会执行拒绝策略;
池化技术的思想主要是为了减少每次获取资源的消耗,提⾼对资源的利用率。
降低资源消耗:通过重复利⽤已创建的线程降低线程创建和销毁造成的消耗,提高线程的重用性。
提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
提高线程的可管理性:线程是稀缺资源,如果⽆限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使⽤线程池可以进⾏统⼀的分配,调优和监控。

ExecutorService是线程池的基本接口,提供了提交任务、关闭线程池的方法;
ScheduledExecutorSerivce是ExecutorService的子接口,在ExecutorService基础上新增了任务调度的功能,可以定时执行任务;
ThreadPoolExecutor是ExecutorService的基础实现类;
ScheduledThreadPoolExecutor继承于ThreadPoolExecutor, 是带任务调度功能的实现类;



由于构造方法参数太多,初学者不易掌握,所以有Executor工具类提供了工厂方法来创建线程池;


特点:
1.参数线程数n是核心线程数,救急线程数为0 ;
2.阻塞队列是无界的,可以放任意数量的任务(救急线程的前提是选择了有界队列)
execute() 添加任务到线程池执行,当核心线程数不够时,则将任务放到阻塞队列中排队:
当任务超过了阻塞队列大小时,再来任务,就会创建maximumPoolSize - corePoolSize 个的救急线程来运行任务(前提是选择有界队列);
救急线程的前提是选择了有界队列;若队列是无界队列,即没有容量限制的队列,则不会创建救急线程;
救急线程有生存时间,类似临时工,任务完毕救急线程就被销毁;而核心线程执行完任务依然保留在线程池中;
当线程到达maximumPoolSize最大线程数仍有新任务时,即核心线程和救急线程都被任务占完了,才会执行拒绝策略;
阻塞队列一般用于生产者、消费者场景,生产者往队列中添加元素,消费者从队列取线程;
在线程池中,通过阻塞队列来实现任务的生产和消费功能;
当队列为空,会阻塞消费者;当队列满了,会阻塞生产者;
阻塞队列有一个非常重要的属性,那就是容量的大小,分为有界和无界两种。
通常,阻塞队列是有界的,例如 ArrayBlockingQueue 如果容量满了,也不会扩容,所以一旦满了就无法再往里放数据了。(救急线程的前提是选择有界队列 !)
无界队列意味着里面可以容纳非常多的元素,例如 LinkedBlockingQueue 的上限是 Integer.MAX_VALUE,约为 2 的 31 次方,是非常大的一个数,可以近似认为是无限容量,因为我们几乎无法把这个容量装满。
缺点:无界队列存在比较大的潜在风险,如果在并发量较大的情况下,线程池中可以几乎无限制的添加任务,容易导致内存溢出的问题!

AbortPoliocy:抛出异常(默认)
CallerRunsPolicy:让调用线程(提交任务的线程)直接执行此任务
DiscardPolicy:放弃本次任务
DiscardOldestPolicy:放弃最早的任务,当前任务取而代之
execute:
参数是Runable类型,无返回结果;

submit:
参数是Callable,实现Callable的线程是有返回值的;
submit方法会返回Future对象,可以用来判断任务是否执行成功;
FutureTask用来在主线程中接收线程池中返回的结果;调用FutureTask对象的**get()**方法获取返回值;

invokeAll:
接收一个集合,执行集合中所有的任务,
返回LIst集合,集合中是Future,

invokeAny:
不会执行所有任务,而是找到最先执行的任务;
只要集合中有一个任务执行成功,就把整个任务的结果作为返回最终的结果返回,其他的取消;
shutdown:
不会接收新的任务,但会处理阻塞队列中剩余任务;
shutdownNow:
将线程池的状态变成stop,即不仅不接受新任务,还会将正在执行的任务停下来 !
正在执行的任务会用interrupt方法打断 ! 并将任务返回
