共计 3336 个字符,预计需要花费 9 分钟才能阅读完成。
$1. 概述
线程池产生背景:高并发我的项目中常常须要同时启用大量线程,因而须要创立大量线程,而频繁的创立新线程和杀死旧线程将会大大拖慢 CPU 的性能,因而利用线程池一次性初始化若干线程,应用时从池中弹出线程,执行完线程池再予以回收
线程池的工作流程:次要是管制运行的线程数量,解决过程中将工作放入队列,而后在线程创立后启动这些工作,如果线程数量超过了最大数量,超出数量的线程排队等待,等其余线程执行结束,再从队列中取出工作执行
线程池的次要特点:实现线程复用、管制最大并发数;可能系统地治理线程
线程池的劣势:
- 升高资源耗费,通过反复利用已创立的线程升高线程频繁创立和销毁的损耗
- 进步响应速度,当工作达到时,工作能够不须要期待线程创立就能立刻执行
- 进步线程的可管理性,线程池能够对线程进行对立的调配调度、调优以及监控
$2. 线程池的实现形式
2.1 通过 Executors 工具类
- Executors.newFixedThreadPool(int nThreads):固定线程数量线程池
// 源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable\>());
}
- Executors.newSingleThreadExecutor():单线程线程池
// 源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable\>()));
}
- Executors.newCachedThreadExecutor():可扩容线程池,实践上反对 Integer.MAX_VALUE 条线程
// 源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX\_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable\>());
}
本质上,这三种线程池尽管各有特点,但底层实现的都是 ThreadPoolExecutor 类,也即最正统的线程池,线程池本池☺
- 理论应用
// 应用案例
import java.util.concurrent.Executors;
public class NewFixedThreadPoolDemo {public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(5);
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
// ExecutorService threadPool = Executors.newCachedThreadExecutor();
for(int i=0; i<10; i++){threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t 办理业务"));
}
threadPool.shutdown(); // 敞开资源!!}
}
2.2 通过 ThreadPoolExecutor[线程池本池!用它!用它! 用它!]
WHY?(谁说的不能用 Executors?)
- 马云曰:我说的!(阿里编程标准明确示意不倡议外部应用 Executors 工具类实现线程池)
- fixThreadPool 和 SingleThreadPool 设置的工作申请队列为 Integer.MAX_VALUE,可能会沉积大量的申请,从而导致 OOM
- CachedThreadPool 和 ScheduledThreadPool 容许创立的线程数量为 Integer.MAX_VALUE, 可能会创立大量线程,从而导致 OOM
// 代码实现
import java.util.concurrent.*;
public class ThreadPoolExecutorDemo {public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(2,
5,
2L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
for(int i=1; i<=10; i++){
final int temp = i;
threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t 办理业务" + temp));
}
threadPool.shutdown();}
}
参数解析
- corePoolSize:线程池中的常驻外围线程数
- maximumPoolSize:线程池中可能包容同时执行的最大线程数,该值必须大于等于 1
- keepAliveTime:多余的闲暇线程的存货工夫,以后池中线程数量大于 corePoolSize 时,当闲暇线程的闲暇工夫达到 keepAliveTime,多余的线程会被销毁直到剩下 corePoolSize 个
- unit:keepAliveTime的单位
- workQueue:工作队列,被提交但尚未被执行的工作 — 设想银行的等待区
- threadFactory:示意生成线程池中工作线程的线程工厂,用于创立线程,个别应用默认即可
- handler:回绝策略,示意当线程池和队列都满了,线程池采取何种策略来拒绝请求执行的 Runnable 工作
$3. 线程池工作原理
talk is cheap, show me the diagram
线程池个别工夫都只会开启外围线程数量的线程,而当工作队列满了之后,线程池会启用非核心线程池区(这里形容只是逻辑分区,实际上线程之间都是等同位置),创立线程并执行工作;而当工作队列中工作变少,有些线程开始闲置,闲置工夫达到设置的 keepAliveTime 后,线程池会登记回收这些闲置线程,直到线程数量复原外围线程数
- 一个线程池可能并发解决的最大工作数 = maximumPoolSize + workQueue 设置的长度
$4. 回绝策略 (渣男[女] 必备)
共有四种回绝策略,以 2.2 中的代码为准,即设置外围线程数为 2、最大线程数为 5、工作队列容量为 3、工作数为 10;见四种策略别离的运行后果如下
- AbortPolicy:当无奈执行工作时,间接抛出 RejectedExecutionException 中断程序
- CallerRunsPolicy:调用者运行机制,该策略不会摈弃工作,也不会抛出异样,而是将无奈执行的工作返还给调用者执行,这里因为是 main 线程调用的,因而返还给 main 线程执行工作。如下图,工作 9 被返还给 main 线程执行了,这过程的工夫内线程池可能曾经执行完工作 1 了,工作队列从而空了一个地位,工作 10 便间接进入工作队列没有返还给 main 线程。在保障工作完全性的状况下,该策略是惟一策略
- DiscardOldestPolicy:摈弃队列中期待最久的工作,而后把当前任务退出到队列中尝试再次提交当前任务
- DiscardPolicy:该策略会抛弃无奈解决的工作,不做任何解决也不抛异样,如果容许工作失落,这是最好的一种策略
REFERENCE
尚硅谷周阳老师 JUC 课程:https://www.bilibili.com/vide…
正文完