1 .线程池主要核心原理和优势
1.1线程池核心原理
- 创建一个池子,池子当中是空的
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程会归还给池子,下次再次提交任务时,不需要创建新的线程,直接复用已有的线程即可。
- 如果提交任务时,池子中没有空闲线程,也无法创建新的线程,队伍就会排队等待。
1.2线程池主要优势
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2. 线程池的代码实现
2.1通过Executors线程池工具类通过调用方法创建不同类型的线程池对象。
//代码实现
/**
* 创建一个没有上限的线程池
*/
ExecutorService executorService = Executors.newCachedThreadPool();
/**
* 往线程池中提交任务
*/
LoopTread loopTread = new LoopTread();
executorService.submit(loopTread);
executorService.submit(loopTread);
executorService.submit(loopTread);
executorService.submit(loopTread);
/**
* 销毁线程池
* 此方法一般不用,因为线程池一般在项目中不进行销毁,随时会有任务
*/
executorService.shutdown();
/**
* 创建一个有上限的线程池
* 3代表这个线程最多只能同时有三个线程
*/
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
/**
* 提交任务
* 提交五个任务,从控制台输出可以看出线程的复用
*/
executorService1.submit(loopTread);
executorService1.submit(loopTread);
executorService1.submit(loopTread);
executorService1.submit(loopTread);
executorService1.submit(loopTread);
一共五个任务,但确只使用了三个线程,这就是线程池里的代码复用。
2.2自定义创建线程池(创建ThreadPoolExecutor类)
它最长的构造方法有七个参数。
- 核心线程数量——在线程池当中无论空闲多久都不会被删除的线程
- 线程池当中最大的线程数量——线程池当中最大能创建的线程数量
- 空闲时间(数值)——临时线程(线程池中出核心线程之外的线程)空闲了多久就会被淘汰的时间。
- 空闲时间(单位)——临时线程空闲了多久就会被淘汰的时间单位,要用枚举类TimeUnit类作为参数
- 阻塞队列——就是创建一个阻塞队列作为参数传入,就是当线程池当中线程数量已经达到了最大线程数量,允许多少个任务排队获取线程,其余的用参数七那个方案来处理。
- 创建线程的方式——不是new一个线程,而是传入一个线程工厂(例如:Executors工具类中的defaultThreadFactory方法返回的就是一个线程工厂)
- 要执行的任务过多时的解决方案——当等待队列中也排满时要怎么处理这些任务(任务拒绝策略)。
//代码实现
/**
* 之前用工具类进行创建,有好多参数不能自己设置
* 咱直接自己手动创建一个线程池,自己设置参数
* 参数一:核心线程数量 不能小于0
* 参数二:最大线程数 不能小于0,数值大于等于核心线程数量
* 参数三:空闲临时线程最大存活时间(数值) 不能小于0
* 参数四:空闲临时线程最大存活时间(单位) 用TimeUnit这个枚举类表示
* 参数五:任务队列,也就是一个堵塞队列 不能为null
* 参数六:创建线程的工厂 不能为null
* 参数七:任务的拒绝策略 不能为null
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3, // 核心线程数量
6, //最大线程数
60, //空闲临时线程最大存活时间(数值)
TimeUnit.SECONDS,//空闲临时线程最大存活时间(单位)
new ArrayBlockingQueue<>(3),//任务队列,也就是一个堵塞队列,也可以使用LinkedBlockingQueue这个阻塞队列
Executors.defaultThreadFactory(),//用线程池工具类Executors创建线程的工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略中其中一个,丢弃任务并抛出RejectedExecutionException
);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
}
上面的代码我们设置最大线程数量为6,而阻塞队列可以排三个,说明当同时有超过9个任务需要执行,第10个线程就会执行拒绝策略,我设置的策略为丢弃任务,并抛出异常RejectedExecutionException。下面有结果就可以证明我们的猜测。
74行恰好是我们放入线程池中第10个任务,所以第74行抛出了RejectedExecutionException异常。
自定义线程在不断的提交任务时有三个临界点:
- 当核心线程满时,再提交队伍就会在阻塞队列中排队
- 当核心线程满了,阻塞队列中也满了,才会创建临时线程
- 当核心线程满了,阻塞队列满了,临时线程也满了,会触发任务拒绝策略,也就是参数七