线程池整理汇总
它山之石,可以攻玉。借鉴整理线程池相关文章,以及自身实践。
文章目录
- 1. 线程池概述
- 2. 线程池UML架构
- 3. Executors创建线程的4种方法
- 3.1 newSingleThreadExecutor
- 3.2 newFixedThreadPool
- 3.3 newCachedThreadPool
- 3.4 newScheduledThreadPool
- 小结
- 4. 线程池标准创建方式-ThreadPoolExecutor
- 4.1 线程池参数
- 4.2 参数执行流程
- 4.3 参数设置
- 4.4 线程池提交任务的两种方式
- 4.5 ThreadFactory(线程工厂)
- 4.6 任务阻塞队列
- 4.7 线程池的拒绝策略
- 4.8 调度器的钩子方法
- 5. ScheduledExecutorService
- 6. 异步调用
- 7. 线程池的关闭
- 8. 面试题
- 9. 美团实践-动态线程池
- 10.线程池的使用场景
- 文章引用
1. 线程池概述
前面介绍了 Java 线程用法,但是当任务量特别大且任务执行时间比较长的时候,创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率。而且,创建的线程无法进行统一管理。
例如:创建线程消耗时间 T1,执行任务消耗时间 T2,销毁线程消耗时间 T3。如果 T1+T3 > T2,那么是不是说开启一个线程来执行这个任务太不划算了!正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了 T1+T3 带来的系统开销。
线程池优点:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 可以根据系统的需求和硬件环境灵活的控制线程的数量,对所有线程进行统一的管理和控制,从而提高系统的运行效率。
2. 线程池UML架构
1. Executor
Executor
:线程池框架最基础的任务执行接口,Executor 框架中几乎所有类都直接或者间接实现 Executor 接口,该接口提供了一种将任务提交
与任务执行
分离开来的机制,该接口只有一个方法execute()
,用来执行已提交的线程任务。
public interface Executor {void execute(Runnable command);
}
2. ExecutorService
继承于 Executor 接口,Java异步目标任务接口,对外提供异步任务的接收服务。扩展了对任务各种操作的接口,该接口是我们最常用的线程池接口。
public interface ExecutorService extends Executor {<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException;<T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException;<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
3. AbstractExecutorService
抽象类,实现了ExecutorService,提供 ExecutorService 执行方法的默认实现,该类实现了submit、invokeAny、invokeAll等方法,并且提供了 newTaskFor 方法返回一个 RunnableFuture 对象。
4. ThreadPoolExecutor
线程池实现类,继承于AbstractExecutorService,JUC线程池的核心实现类。
5. ScheduledThreadPoolExecutor
继承于ThreadPoolExecutor,实现了 ExecutorService 中延时执行
和定时执行
等抽象方法。
6. Executors
静态工厂类,它通过静态工厂方法返回ExecutorService、ScheduledExecutorService等线程池示例对象。
3. Executors创建线程的4种方法
Executors
工具类是 Executor 框架的一个工具帮助类,提供了4种创建线程池的方式,这4种方式都是直接或间接通过ThreadPoolExecutor
来实现的,一般情况下我们可以通过该工具类来创建线程池,如果该工具类的几个方法满足不了的情况下,我们可以自定义实现。
3.1 newSingleThreadExecutor
- public static ExecutorService
newSingleThreadExecutor()
- public static ExecutorService
newSingleThreadExecutor
(ThreadFactory threadFactory
)
newSingleThreadExecutor(ThreadFactory threadFactory)
源码:
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));
}
阻塞队列:LinkedBlockingQueue
任务类 MyRunnable
/*任务类,包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable implements Runnable {private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务..." + id);}
}
ThreadPoolDemo示例:
public class ThreadPoolDemo {public static void main(String[] args) {test1();test2();}private static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "自定义的线程名称-" + n++);}});//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}}
特点:
- 只有一个线程的线程池
- 单线程线程池中的任务是按照提交的次序顺序执行的
- 池中的唯一线程的存活时间是无限的
- 当池中的唯一线程正繁忙时,新提交的任务实例会进入内部的阻塞队列中,并且其阻塞队列
LinkedBlockingQueue
是无界的。
适用场景:
- 任务按照提交次序,一个任务一个任务地逐个执行的场景
3.2 newFixedThreadPool
- public static ExecutorService
newFixedThreadPool(int nThreads)
- public static ExecutorService
newFixedThreadPool
(int nThreads
,ThreadFactory threadFactory
)
newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
源码:
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
}
阻塞队列:LinkedBlockingQueue
ThreadPoolDemo示例:
public class ThreadPoolDemo {public static void main(String[] args) {test1();test2();}private static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3);//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3, new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "自定义的线程名称-" + n++);}});//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}}
特点:
- 如果线程数没有达到“固定数量”,每次提交一个任务线程池内就创建一个新线程,直到线程达到线程池固定的数量。
- 线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- 在接收异步任务的执行目标实例时,如果池中的所有线程均在繁忙状态,新任务会进入阻塞队列
LinkedBlockingQueue
(无界)。
适用场景:
- 需要任务长期执行的场景
- CPU密集型任务
缺点:
- 内部使用无界队列来存放排队任务,当大量任务超过线程池最大容量需要处理时,队列无限增大,使服务器资源迅速耗尽。
3.3 newCachedThreadPool
- public static ExecutorService
newCachedThreadPool()
- public static ExecutorService
newCachedThreadPool
(ThreadFactory threadFactory
)
newCachedThreadPool(ThreadFactory threadFactory)
源码:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory);
}
默认空闲 60秒 线程回收。阻塞队列:SynchronousQueue
ThreadPoolDemo示例:
public class ThreadPoolDemo {public static void main(String[] args) {test1();test2();}private static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool();//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "自定义的线程名称-" + n++);}});//2:提交任务;for (int i = 1; i <= 5; i++) {es.submit(new MyRunnable(i));}}}
特点:
- 在接收新的异步任务 target 执行目标实例时,如果池内所有线程繁忙,此线程池就会添加新线程来处理任务。
- 不会对线程池大小进行限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- 如果部分线程空闲,也就是存量线程的数量超过了处理任务数量,就会回收空闲(60秒不执行任务)线程。
适用场景:
- 需要快速处理突发性强、耗时较短的任务场景,如 Netty 的 NIO 处理场景、REST API接口的瞬时削峰场景
缺点:
- 线程池没有最大线程数量限制,如果大量的异步任务执行目标实例同时提交,可能会因创建线程过多而导致资源耗尽。
3.4 newScheduledThreadPool
- public static ScheduledExecutorService
newScheduledThreadPool(int corePoolSize)
- public static ScheduledExecutorService
newScheduledThreadPool
(int corePoolSize
,ThreadFactory threadFactory
)
ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(), threadFactory);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);
}
阻塞队列:DelayedWorkQueue
任务类 MyRunnable2:
class MyRunnable2 implements Runnable {private int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();String curTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());System.out.println(name + " " + curTime + " " + "执行了任务..." + id);}
}
ThreadPoolDemo示例:
public class ThreadPoolDemo {public static void main(String[] args) {test1();test2();test3();}private static void test1() {//1:使用工厂类获取线程池对象ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);//2:提交任务;for (int i = 1; i <= 5; i++) {ses.submit(new MyRunnable(i));}}private static void test2() {//1:使用工厂类获取线程池对象ScheduledExecutorService ses = Executors.newScheduledThreadPool(3, new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "自定义的线程名称-" + n++);}});//2:提交任务;for (int i = 1; i <= 5; i++) {ses.submit(new MyRunnable(i));}}private static void test3() {//1:使用工厂类获取线程池对象ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);//2:提交任务;for (int i = 1; i <= 5; i++) {ses.scheduleAtFixedRate(new MyRunnable2(0), 0, 5, TimeUnit.SECONDS);}}}
小结
Executors创建线程池的4种方法十分方便,但是构造器创建普通线程池、可调度线程池比较复杂,这些构造器会涉及大量的复杂参数,已经较少使用。而且4种方式创建的线程池均存在缺点:
newFixedThreadPool
、newSingleThreadExecutor
:阻塞队列无界,队列很大时,堆积大量任务会导致OOM(内存耗尽)。newCachedThreadPool
、newScheduledThreadPool
: 线程数量无上界,会导致创建大量的线程,从而导致OOM,甚至导致CPU线程资源耗尽。
Java 通过 Executors 提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor
的参数实现的。
通常建议直接使用线程池ThreadPoolExecutor
的构造器
4. 线程池标准创建方式-ThreadPoolExecutor
public class ThreadPoolExecutor extends AbstractExecutorService {private volatile int corePoolSize;//核心线程数,即使线程空闲也不会被收回private volatile int maximumPoolSize;//线程的上限private volatile long keepAliveTime;//线程的最大空闲时长private final BlockingQueue<Runnable> workQueue;//任务的排队队列private volatile ThreadFactory threadFactory;//新线程的产生方式private volatile RejectedExecutionHandler handler;//拒绝策略
}
4.1 线程池参数
1. corePoolSize 核心线程数
- 核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
- 如果指定 ThreadPoolExecutor 的
allowCoreThreadTimeOut
属性为true
,那么核心线程如果闲置状态的话,超过一定时间(keepAliveTime
),就会被销毁掉。 - 线程池接收到新任务,当前工作线程数少于 corePoolSize,即使有空闲的工作线程,也会创建新的线程来处理该请求,直到线程数达到corePoolSize。
2. maximumPoolSize 最大线程数量
- 当前工作线程数多于 corePoolSize 数量,但小于 maximumPoolSize 数量,那么仅当任务排队队列已满时才会创建新线程。
- 如果 maximumPoolSize 被设置为无界值(如
Integer.MAX_VALUE
)时,线程池可以接收任意数量的并发任务。
3. keepAliveTime 空闲线程存活时间
- 该线程池中非核心线程闲置超时时长
- 一个非核心线程,如果闲置状态的时长超过这个参数所设定的时长,就会被销毁掉。如果设置
allowCoreThreadTimeOut = true
,则会作用于核心线程。
4. TimeUnit 空闲线程存活时间单位
- keepAliveTime 的单位,TimeUnit是一个枚举类型。MILLISECONDS(毫秒)、SECONDS (秒)等
5. BlockingQueue workQueue 任务队列
- 维护着等待执行的 Runnable 异步任务。
- 当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
6. threadFactory 线程工厂
- 创建线程的方式,实现方法
public Thread newThread(Runnable r)
即可。
7. RejectedExecutionHandler handler 拒绝策略
- workQueue 队列已满,总线程数又达到了 maximumPoolSize,执行拒绝策略。
4.2 参数执行流程
- 线程数量未达到
corePoolSize
,则新建一个线程(核心线程)执行任务。 - 线程数量达到了
corePoolSize
,则将任务移入队列workQueue
等待。 - 队列已满,新建线程(非核心线程)执行任务。
- 队列已满,总线程数又达到了
maximumPoolSize
,执行拒绝策略RejectedExecutionHandler
抛出异常。 - 非核心线程闲置状态时长超过
keepAliveTime
,就会被销毁掉。
4.3 参数设置
首先,需要明白两个系统参数:
- tasks:程序每秒需要处理的最大任务数量
- tasktime:单线程处理一个任务所需要的时间
- responsetime:系统允许任务最大的响应时间
1. corePoolSize
每个任务需要 tasktime 秒处理,则每个线程每秒可处理 1/tasktime 个任务。系统每秒有tasks个任务需要处理,则需要的线程数为:tasks/(1/tasktime)。即 tasks*tasktime 个线程数。具体数字最好根据8020原则,即80%情况下系统每秒任务数。
2. maximumPoolSize
当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200)。
3. keepAliveTime
keepAliveTiime设定值可根据任务峰值持续时间来设定。
5. BlockingQueue workQueue
任务队列的长度要根据核心线程数、系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime
以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间(tasktime)。
7. RejectedExecutionHandler handler 拒绝策略
workQueue 队列已满,总线程数又达到了 maximumPoolSize,执行拒绝策略。
4.4 线程池提交任务的两种方式
1. execute方法
void execute(Runnable command)
:Executor接口方法,接收 Runnable 类型对象。
2. submit方法
<T> Future<T> submit(Callable<T> task)
:ExecutorService接口方法,接收 Callable 类型对象,Callable入参允许任务返回值。<T> Future<T> submit(Runnable task, T result)
:ExecutorService接口方法,接收 Runnable 类型对象。Future<?> submit(Runnable task)
:ExecutorService接口方法,接收 Runnable 类型对象。
区别:
execute()
方法只能接收 Runnable 类型的参数,而submit()
方法可以接收 Callable、Runnable 两种类型的参数。- Callable 类型的任务是可以返回执行结果的,而 Runnable 类型的任务不可以返回执行结果。
submit()
提交任务后会有返回值,而execute()
没有。submit()
方便 Exception 处理execute()
方法在启动任务执行后,任务执行过程中可能发生的异常调用者并不关心。而通过submit()
方法返回的 Future 对象(异步执行实例),可以进行异步执行过程中的异常捕获。
4.5 ThreadFactory(线程工厂)
public interface ThreadFactory {Thread newThread(Runnable r);
}
ThreadFactory
是 Java 线程工厂接口,只有1个方法,调用ThreadFactory的唯一方法newThread()
创建新线程时,可以更改所创建的新线程的名称、线程组、优先级、守护进程状态等。
上述《3. Executors创建线程的4种方法》中,4种方法均可指定ThreadFactory。
Executors为线程池工厂类,用于快捷创建线程池(Thread Pool);ThreadFactory为线程工厂类,用于创建线程(Thread)
4.6 任务阻塞队列
维护着等待执行的 Runnable 对象。一个线程从一个空的阻塞队列中获取元素时线程会被阻塞,直到阻塞队列中有了元素;当队列中有元素后,被阻塞的线程会自动被唤醒。
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。常用的workQueue类型:
LinkedBlockingQueue
:一个基于链表实现的阻塞队列,按 FIFO 排序任务,可以设置容量(有界队列),不设置容量则默认使用Integer.Max_VALUE
作为容量(无界队列)。ArrayBlockingQueue
:一个数组实现的有界阻塞队列(有界队列),队列中的元素按 FIFO 排序,ArrayBlockingQueue在创建时必须设置大小。接收到任务的时候,如果没有达到 corePoolSize 的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了 maximumPoolSize,并且队列也满了,则执行拒绝策略。SynchronousQueue
:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它。PriorityBlockingQueue
:是具有优先级的无界队列。DelayQueue
:队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。
4.7 线程池的拒绝策略
public class RejectedExecutionException extends RuntimeException
1. AbortPolicy:拒绝策略
新任务就会被拒绝,并且抛出RejectedExecutionException异常。该策略是线程池默认的拒绝策略。
必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
public class ThreadPoolDemo {public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {//1:使用线程池对象int corePoolSize = 2;int maximumPoolSize = 5;long keepAliveTime = 60;BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(10);RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);//2:提交任务;for(int i=0; i<100; i++) {try {poolExecutor.execute(new MyRunnable(i));} catch (Exception e) {System.out.println(e.getMessage());}}}
}
结果看出,总共会执行100次任务,但有些任务会抛异常。
poolExecutor.execute()
提交任务,由于会抛出 RuntimeException,如果没有 try…catch 处理异常信息的话,会中断调用者的处理流程,后续任务得不到执行(跑不完100个)。
2. DiscardPolicy:抛弃策略
新任务就会直接被丢掉,并且不会有任何异常抛出。
...
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
...
总共执行任务不足100,大量任务直接被丢弃。
3. DiscardOldestPolicy:抛弃最老任务策略
将最早进入队列的任务抛弃,从队列中腾出空间,再尝试加入队列(一般队头元素最老)。
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
总共执行任务不足100,少量任务被丢弃。
4. CallerRunsPolicy:调用者执行策略
新任务被添加到线程池时,如果添加失败,那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去执行新任务。
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
总共会执行100次任务,部分任务main线程自己执行,输出main执行了任务...15
。
适应场景:一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大。
5. 自定义策略
实现 RejectedExecutionHandler 接口的rejectedExecution()
方法。
public class ThreadPoolDemo {public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {//1:使用线程池对象int corePoolSize = 2;int maximumPoolSize = 5;long keepAliveTime = 60;BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(10);//2:使用自定义拒绝策略,入参ArrayListArrayList<String> list = new ArrayList<>();RejectedExecutionHandler handler = new MyRejectedPolicy(list);ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);//3:提交任务;for (int i = 1; i <= 100; i++) {poolExecutor.execute(new MyRunnable(i));}//4:打印ArrayList内具体拒绝任务Thread.sleep(2000);System.out.println(list);}static class MyRejectedPolicy<T extends List> implements RejectedExecutionHandler {public T t;public MyRejectedPolicy(T t) {this.t = t;}@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {//被拒绝的任务写入List集合,当然也可以写入文件、数据库等等System.out.println(executor.getTaskCount());t.add(executor.getTaskCount());}}
}
6. 第三方实现的拒绝策略
- dubbo中的线程拒绝策略:输出了一条警告级别的日志,输出当前线程堆栈详情,继续抛出拒绝执行异常。
- Netty中的线程池拒绝策略:Netty中的实现很像JDK中的CallerRunsPolicy,舍不得丢弃任务。不同的是,CallerRunsPolicy是直接在调用者线程执行的任务。而 Netty是新建了一个线程来处理的。
- activeMq中的线程池拒绝策略:activeMq中的策略属于最大努力执行任务型,当触发拒绝策略时,在尝试一分钟的时间重新将任务塞进任务队列,当一分钟超时还没成功时,就抛出异常。
小结:
四种拒绝策略是相互独立无关的,选择何种策略去执行,还得结合具体的业务场景。实际工作中,一般直接使用 ExecutorService 的时候,都是使用的默认的 defaultHandler ,也即 AbortPolicy 策略。
4.8 调度器的钩子方法
三个钩子方法存在于 ThreadPoolExecutor 类,这3个方法都是空方法,一般会在子类中重写。
- protected void
beforeExecute
(Thread t, Runnable r) { }: 任务执行之前的钩子方法 - protected void
afterExecute
(Runnable r, Throwable t) { }: 任务执行之后的钩子方法 - protected void
terminated()
{ }: 线程池终止时的钩子方法
public class ThreadPoolDemo {public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {//1:使用线程池对象ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2)){@Overrideprotected void terminated(){System.out.println("调度器已停止...");}@Overrideprotected void beforeExecute(Thread t,Runnable target){System.out.println("前钩执行...");super.beforeExecute(t, target);}@Overrideprotected void afterExecute(Runnable target,Throwable t){System.out.println("后钩执行...");super.afterExecute(target, t);}};//2:提交任务;for (int i = 1; i <= 5; i++) {poolExecutor.submit(new MyRunnable(i));}}
}
5. ScheduledExecutorService
6. 异步调用
7. 线程池的关闭
shutdown()
:等待当前工作队列中的剩余任务全部执行完成之后,才会执行关闭,但是此方法被调用之后,线程池不会再接收新的任务。shutdownNow()
:立即关闭线程池的方法,此方法会打断正在执行的工作线程,并且会清空当前工作队列中的剩余任务,返回的是尚未执行的任务。awaitTermination()
:等待线程池完成关闭,shutdown()
、shutdownNow()
方法之后,用户程序都不会主动等待线程池关闭完成。
8. 面试题
线程池详解
动态线程池有什么意义?
以前的传统软件当中,单机部署,硬件部署,确实,我们能使用的线程数取决于服务器的核心线程数,而且基本没有其他服务来争抢这些线程。
但是现在是容器的时代,云原生的时代。
多个容器部署在一个宿主机上,那么当高峰期的时候,某个容器就需要占用大量的cpu资源,如果所有的容器都将大部分资源占据,那么这个容器必然面临阻塞甚至瘫痪的风险。
当高峰期过了,释放这部分资源可以被释放掉,用于其他需要的容器。。
再结合到目前的云服务器节点扩容,都是需要动态扩缩容的的,和线程相似,在满足高可用的情况下,尽量的节约成本。
9. 美团实践-动态线程池
Java线程池实现原理及其在美团业务中的实践
10.线程池的使用场景
线程池的使用场景
文章引用
Java线程池(超详细)
Java 多线程:Executor、ExecutorService、Executors、Callable、Future与FutureTask
线程池详解(通俗易懂超级好)
线程池阻塞队列满了该怎么办,线上宕机了队列里的请求会丢吗
相关文章:

线程池整理汇总
它山之石,可以攻玉。借鉴整理线程池相关文章,以及自身实践。 文章目录1. 线程池概述2. 线程池UML架构3. Executors创建线程的4种方法3.1 newSingleThreadExecutor3.2 newFixedThreadPool3.3 newCachedThreadPool3.4 newScheduledThreadPool小结4. 线程池…...

华为OD机试真题Python实现【最短木板长度】真题+解题思路+代码(20222023)
🔥系列专栏 华为OD机试(Python)真题目录汇总华为OD机试(JAVA)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明示例二输入输出说明...

VMware安装CentOS7
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。个人爱好: 编程,打篮球,计算机知识个人名言:海不辞水,故能成其大;山不辞石…...

力扣24.两两交换链表中的节点
文章目录力扣24.两两交换链表中的节点题目描述方法1:非递归方法2:递归力扣24.两两交换链表中的节点 题目描述 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&…...

AtCoder Regular Contest 137 题解(A~C)
A-Coprime Pair 思路 我们知道两个质数之间并不会相隔太远,于是我们直接用暴力就可以通过这题。 先从大到小枚举答案,并且枚举所有可能的起点,当枚举到的两个值满足条件输出并结束程序即可。 代码 #include <bits/stdc.h> using n…...

【C语言】预处理指令
C语言预处理指令一、什么是预处理指令二、预处理指令特点三、文件包含四、C标准库<stdio.h>一、什么是预处理指令 C语言的源文件(.c文件)需要经过编译生成可执行程序,编译操作会将源文件转换成目标文件,对于 VC、VS&#x…...

Java基础之多线程JUC全面学习笔记
目录初识多线程多线程的实现方式常见的成员方法线程安全的问题死锁生产者和消费者线程池自定义线程池初识多线程 什么是多线程? 线程 线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程中的实际运作单位。 简单理解:应用软件中互相独立&…...

13.CSS文本样式
文本样式 h1 {color: blue; }● 回顾上一节的内容,我们让h1标题的文字变成了蓝色,注意如果html中有多个h1标签,那我们这种写法所有的h1标签都会变成蓝色,除了颜色,本节我们将学习更多的CSS属性 文字大小font-size h…...

西恩科技更新招股书:IPO前大手笔分红“套现”, 赵志安为实控人
2月14日,上海西恩科技股份有限公司(下称“西恩科技”)更新了招股书(申报稿)。据贝多财经了解,西恩科技于2022年8月12日递交上市申请材料,准备在创业板上市,此次是西恩科技第二次更新…...

【CentOS】有关时间的设置
目录环境信息date语法信息查看时间设置时间设置日期tzselecttimedatectl语法显示当前及所有时区修改时区hwclock语法读取硬件时钟使用硬件时钟设置系统时间使用系统时间设置硬件时钟如何理解硬件时钟和系统时钟环境信息 CentOS 7 date 语法信息 date --help用法:…...

OpenCV制作Mask图像掩码
一、掩膜(mask) 在有些图像处理的函数中有的参数里面会有mask参数,即此函数支持掩膜操作,首先何为掩膜以及有什么用,如下: 数字图像处理中的掩膜的概念是借鉴于PCB制版的过程,在半导体制造中&am…...

C++STL剖析(九)—— unordered_map和unordered_multimap的概念和使用
文章目录1. unordered_map的介绍和使用🍑 unordered_map的构造🍑 unordered_map的使用🍅 insert🍅 operator[ ]🍅 find🍅 erase🍅 size🍅 empty🍅 clear🍅 sw…...

Android无菜单键,如何触发onCreateOptionsMenu(Menu menu)
文章目录小结问题及解决无法触发onCreateOptionsMenu(Menu menu)修改配置文件解决使用一个按钮来触发其它办法参考小结 现在的Android有三个键: 任务键,Home键,返回键,也就是没有菜单键了,那么如何如何触发onCreateOp…...

“黑洞”竟是外星人的量子计算机?
宇宙中的黑洞可以用作终极量子计算机,我们可以从中探索它们的特征。(图片来源:网络)我们完全有理由怀疑生命在我们的宇宙中很常见,但是为什么我们从未发现过其他生命存在的迹象?这个问题几乎自现代天文学诞…...

计算机网络入门
一,计算机网络在信息时代中的作用 21世纪的一些重要特征就是数字化,网络化和信息化,它是一个以网络为核心的信息时代。有三类大家很熟悉的网络,即电信网络,有线电视网络和计算机网络。按照最初的服务分工,…...

网络安全-内网DNS劫持-ettercap
网络安全-内网DNS劫持-ettercap 前言 一,我也是初学者记录的笔记 二,可能有错误的地方,请谨慎 三,欢迎各路大神指教 四,任何文章仅作为学习使用 五,学习网络安全知识请勿适用于违法行为 学习网络安全知识请…...

synchronized和Lock的区别
synchronized和lock的区别 synchronized和Lock,我已经通过源码级别的介绍过了,下面我们来总结下他们的区别 区别: 1.synchronized是关键字,Lock是接口,synchronized是JVM层实现,Lock是JDK中JUC包下的实现;…...

SpringBoot 指标监控 Actuator
Spring Boot Actuator为 Micrometer 提供了依赖管理和自动配置,Micrometer是一个支持 众多监控系统 的应用程序指标接口 该功能与:java\jdk\bin 下的 Jconsole 功能雷同 1、pom文件中引入依赖(使用的springboot是2.7.2) <dep…...

面试浅谈之十大排序算法
面试浅谈之十大排序算法 HELLO,各位博友好,我是阿呆 🙈🙈🙈 这里是面试浅谈系列,收录在专栏面试中 😜😜😜 本系列将记录一些阿呆个人整理的面试题 🏃&…...

LeetCode-1250. 检查「好数组」【数论,裴蜀定理】
LeetCode-1250. 检查「好数组」【数论,裴蜀定理】题目描述:解题思路一:裴蜀定理是:a*xb*y1。其中a,b是数组中的数,x,y是任意整数。如果a,b互质那么一定有解。问题即转换为寻找互质的数。解题思路二:简化代码…...

【Linux】NTP时间同步服务与NFS网络文件共享存储服务器(配置、测试)
一、NTP时间同步服务1、NTP介绍NTP服务器【Network Time Protocol(NTP)】是用来使计算机时间同步化的一种协议,它可以使计机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正&a…...

windows下php连接oracle安装oci8扩展报错(PHP Startup: Unable to load dynamic library ‘oci8_11g‘)
记录一下php7.29安装oci8的艰苦过程,简直就是唐僧西天取经历经九九八十一难。 使用的是phpstudy_pro安装的ph扩展wnmp环境下; 1 、安装oralce Instant Client 首先,安装oci8和pdo_oci扩展依赖的Oracle client。了解到需要连接的Oracle版…...

TensorRT的功能
TensorRT的功能 文章目录TensorRT的功能2.1. C and Python APIs2.2. The Programming Model2.2.2. The Runtime Phase2.3. Plugins2.4. Types and Precision2.5. Quantization2.6. Tensors and Data Formats2.7. Dynamic Shapes2.8. DLA2.9. Updating Weights2.10. trtexec本章…...

433MHz无线通信--模块RXB90
1、接收模块RXB90简介 两个数据输出是联通的。 2、自定义一个编码解码规则 组数据为“0x88 0x03 0xBD 0xB6”。 3、发射模块 如何使用示波器得到捕捉一个周期的图像? 通过date引脚连接示波器CH1,以及示波器探针的接地端接芯片的GND,分…...

Seata源码学习(三)-2PC核心源码解读
Seata源码分析-2PC核心源码解读 2PC提交源码流程 上节课我们分析到了GlobalTransactionalInterceptor全局事务拦截器,一旦执行拦截器,我们就会进入到其中的invoke方法,在这其中会做一些GlobalTransactional注解的判断,如果有注解…...

IO流概述
🏡个人主页 : 守夜人st 🚀系列专栏:Java …持续更新中敬请关注… 🙉博主简介:软件工程专业,在校学生,写博客是为了总结回顾一些所学知识点 目录IO流概述IO 流的分类总结流的四大类字…...

【node.js】node.js的安装和配置
文章目录前言下载和安装Path环境变量测试推荐插件总结前言 Node.js是一个在服务器端可以解析和执行JavaScript代码的运行环境,也可以说是一个运行时平台,仍然使用JavaScript作为开发语言,但是提供了一些功能性的API。 下载和安装 Node.js的官…...

Python优化算法—遗传算法
Python优化算法—遗传算法一、前言二、安装三、遗传算法3.1 自定义函数3.2 遗传算法进行整数规划3.3 遗传算法用于旅行商问题3.4 使用遗传算法进行曲线拟合一、前言 优化算法,尤其是启发式的仿生智能算法在最近很火,它适用于解决管理学,运筹…...

数据埋点(Data buried point)的应用价值剖析
一、什么是数据埋点?数据埋点指在应用中特定的流程中收集一些信息,用来跟踪应用使用的状况,后续用来进一步优化产品或是提供运营的数据支撑。比如访问数(Visits),访客数(Visitor),停…...

一文弄懂硬链接、软链接、复制的区别
复制 命令:cp file1 file2 作用:实现对file1的一个拷贝。 限制:可以跨分区,文件夹有效。 效果:修改file1,对file2无影响;修改file2,对file1无影响。删除file1,对file…...