线程池的好处
- 降低资源的消耗
- 提高响应的速度
- 方便管理
- 线程复用,可以控制最大并发数,管理线程
创建线程池的三大方法
线程池不允许使用 Executors 去创建,而是通过 ThreeadPoolExecutor 的方式
- Executors.newSingleThreadExecutor(); // 单个线程
- Executors.newFixedThreadPool(int nThreads); //创建一个固定的线程池的大小
- Executors.newCachedThreadPool(); //缓存池,可伸缩的,遇强则强,遇弱则弱
Executors 创建的弊端:
- FixedThreadPool 和 SingleThreadPool
a. 允许的请求队列长度为integer的最大值,约21亿,可能会堆积大量的请求,导致OOM - CachedThreadPool 和 ScheduledThreadPool
a. 允许创建的线程数量为Integer的最大值,可能会创建大量的线程,而从导致OOM
创建线程池七大参数
- corePoolSize: 核心线程池最小可同时运行的线程数量
- maximumPoolSize: 存放任务达到列队容量时,当前可同时运行的最大线程数量
- workQueue: 阻塞队列,如果达到核心线程数,新任务会被放入队列中
- keepAliveTime: 线程池中数量大于 corePoolSize 时,如果没有新的任务,核心线程外的线程不会立即销毁,而是等待该设置时间后才会被销毁
- unit: 等待时间单位
- threadFactory: 线程工厂,创建线程用,一般不动
- handler: 拒绝策略
四大拒绝策略
当线程池的任务缓存队列已满且线程池中的线程数量达到最大线程池大小是,如果还有任务来则会采取拒绝策略
- 丢弃任务并抛出异常 (线程池默认的拒绝策略)
- 丢弃任务,但是不抛出异常
- 队列满了,丢弃最早未执行的任务
- 由调用线程处理该任务,哪里来的去哪里
最大线程池如何定义
- CPU密集型, 几核就是几,可以保持CPU的效率最高
- 获取CPU的核数 Runtime.getRuntime().availableProcessors()
- IO 密集型, 判断程序中是否耗 IO 的线程
线程池中的线程创建流程
线程池默认初始化后不启动 Worker, 等待有请求时才会,当我们调用execute()方法添加一个任务时,线程池会作如下判断
- 如果正在运行的线程数量小于核心线程池大小,那么马上创建线程运行这个任务
- 如果正在运行的线程数量大于或等于核心线程池大小,那么将这个任务放入队列
- 如果此时队列满了,且正在运行的线程数量小于最大线程池大小数,那么还是要创建非核心线程立即运行这个任务
- 如果队列满了,且正在运行的线程数量大于或等于最大线程池大小数,那么线程池会抛出异常
- 当一个线程完成任务时,会从队列里取出下一个任务来执行,当一个线程无事可做,超过一定时间,线程池会判断,如果前端的运行线程数大于核心线程池大小数,那么该线程就会被停掉.当完成所有任务后,最终会收缩到设置的核心线程池大小数