并发复习笔记之第九章(多线程并发之线程池)
想看后续请持续关注
以下来源有书籍 深入理解 JVM 虚拟机,java 并发编程的艺术,深入浅出多线程,阿里巴巴技术手册以及一些公众号 CS-Notes,JavaGuide,以及一些大厂高频面试题吐血总结,以及狂神说视频笔记,目的在于通过问题来复习整个多线程,已下是全部章节,觉得不错点个赞评论收藏三连一下,您的鼓励就是我继续创作的最大动力!!!!
一.多线程基础
二.多线程基础之 volatile
三.多线程基础之synchronized
四.多线程使用及线程间的通信
五.多线程进阶之锁
六.多线程并发之容器
七.原子类和 CAS
八.并发工具类
九.线程池
9. 多线程并发之线程池 ⭐️
9.1 为什么要用线程池?
-
降低资源消耗 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
-
提高响应速度 当任务到达时,任务可以不需要等到线程创建就能立即执行。
-
提高线程可管理性 使用线程池可以进行统一分配、调优和监控。
9.2 使用线程池
9.2.1 线程池的创建
9.2.1.1 使用Executors 实现
- Executors.newSingleThreadExecutor()
创建单条线程处理任务
- Executor.newFixedThreadPool()
该⽅法返回⼀个固定线程数量的线程池。
- Executor.CachedThreadPool()
可以无限扩大的线程池
- Executors.ScheduledThreadPool()
实现周期性的线程调度
9.2.1.2 使用Executors 实现的缺点
说明:Executors 返回的线程池对象的弊端如下(阿里巴巴开发手册)
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM 。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM 。
9.2.2 使用ThreadPoolExecutor 创建
9.2.2.1 线程池的七大参数
四个重要参数
- corePoolSize 核心线程数量 = 银行每天上班的服务员
- maximumPoolSize 最大线程个数 = 银行加班的服务员 + 每天上班
- workQueue 阻塞队列 = 银行的等待的椅子
- RejectedExecutionHandler 达到最大 = max + workqueue
三个不重要参数
- keepAliveTime 等待时间 = 超过时间加班人员下班
- threadFactory 构造Thread的方法,一般不变
- TimeUnit unit 等待时间单位
9.2.2.2 线程池的增长策略
- 可以通过以下代码去实践
public class Juc {
public static void main(String[] args) throws InterruptedException { // 总数是6,必须要执行任务的时候,再使用!
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
3,// 核心
5,//固定最大
3,//等待时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try { // 最大承载:Deque + max // 超过 RejectedExecutionException
for (int i = 1; i <= 6; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
}
);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
- 当线程数x,x<= 2(corePoolSize)每提交一个线程就创建一个线程(需要获取全局锁)
- 当线程数x,2 <= x 每提交一个任务,将任务加入阻塞队列(大部分都是在执行这一步骤,不需要获取全局锁)
- 当线程数x, 2 < x ,并且阻塞队列已经满了,启动非核心线程执行任务(需要获取全局锁)
- 当线程数x,max = x 之后考虑拒绝策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T98VXpoS-1618567555351)(/Users/yazhouheilong/Library/Application Support/typora-user-images/截屏2021-04-16 16.58.38.png)]
9.2.2.3 线程池的拒绝策略
(当任务大于 max + queue)
- AbortPolicy 直接抛弃,抛出异常
- DiscardPolicy 直接丢弃,不抛出异常
- CallerRunsPolicy 调用该execute的线程来执行(main线程)
- DicardOldestPolicy 如果队列头部任务还在执行,抛弃队列的头部的任务,执行当前任务(可以看源码)
9.2.2.4 线程池的阻塞队列
- ArrayBlockQueue 有界的阻塞队列 有界队列能增加系统的稳定性和预警能力(推荐使用 )
- LinkedBlockQueue 无界的阻塞队列 maximumPoolSize可能没用 设置成无界队列,那么线程池的队列就会越来越多, 有可能会撑满内存,导致整个系统不可用
9.2.3 向线程池提交任务的两种方法区别
- execute()
execute()方法用于不需要返回值的
- submit()
submit() 有返回值,通过这个 future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值
9.2.4 关闭线程池
- shutdown()
9.2.5 配置线程池
暂略