北滘网站建设公司,建站公司网站,荆门网站建设514885,设计模版网站它山之石#xff0c;可以攻玉。借鉴整理线程池相关文章#xff0c;以及自身实践。 文章目录1. 线程池概述2. 线程池UML架构3. Executors创建线程的4种方法3.1 newSingleThreadExecutor3.2 newFixedThreadPool3.3 newCachedThreadPool3.4 newScheduledThreadPool小结4. 线程池…它山之石可以攻玉。借鉴整理线程池相关文章以及自身实践。 文章目录1. 线程池概述2. 线程池UML架构3. Executors创建线程的4种方法3.1 newSingleThreadExecutor3.2 newFixedThreadPool3.3 newCachedThreadPool3.4 newScheduledThreadPool小结4. 线程池标准创建方式-ThreadPoolExecutor4.1 线程池参数4.2 参数执行流程4.3 参数设置4.4 线程池提交任务的两种方式4.5 ThreadFactory线程工厂4.6 任务阻塞队列4.7 线程池的拒绝策略4.8 调度器的钩子方法5. ScheduledExecutorService6. 异步调用7. 线程池的关闭8. 面试题9. 美团实践-动态线程池10.线程池的使用场景文章引用1. 线程池概述
前面介绍了 Java 线程用法但是当任务量特别大且任务执行时间比较长的时候创建/销毁线程伴随着系统开销过于频繁的创建/销毁线程会很大程度上影响处理效率。而且创建的线程无法进行统一管理。
例如创建线程消耗时间 T1执行任务消耗时间 T2销毁线程消耗时间 T3。如果 T1T3 T2那么是不是说开启一个线程来执行这个任务太不划算了正好线程池缓存线程可用已有的闲置线程来执行新任务避免了 T1T3 带来的系统开销。线程池优点
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。可以根据系统的需求和硬件环境灵活的控制线程的数量对所有线程进行统一的管理和控制从而提高系统的运行效率。 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 FutureT submit(CallableT task);T FutureT submit(Runnable task, T result);Future? submit(Runnable task);T ListFutureT invokeAll(Collection? extends CallableT tasks)throws InterruptedException;T ListFutureT invokeAll(Collection? extends CallableT tasks,long timeout, TimeUnit unit)throws InterruptedException;T T invokeAny(Collection? extends CallableT tasks)throws InterruptedException, ExecutionException;T T invokeAny(Collection? extends CallableT tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}3. AbstractExecutorService 抽象类实现了ExecutorService提供 ExecutorService 执行方法的默认实现该类实现了submit、invokeAny、invokeAll等方法并且提供了 newTaskFor 方法返回一个 RunnableFuture 对象。
4. ThreadPoolExecutor 线程池实现类继承于AbstractExecutorServiceJUC线程池的核心实现类。
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 LinkedBlockingQueueRunnable(),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 LinkedBlockingQueueRunnable(),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 SynchronousQueueRunnable(),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,BlockingQueueRunnable 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 BlockingQueueRunnable 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 FutureT submit(CallableT task)ExecutorService接口方法接收 Callable 类型对象Callable入参允许任务返回值。T FutureT 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 PoolThreadFactory为线程工厂类用于创建线程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;BlockingQueueRunnable workQueue new LinkedBlockingQueueRunnable(10);RejectedExecutionHandler handler new ThreadPoolExecutor.AbortPolicy();ThreadPoolExecutor poolExecutor new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);//2:提交任务;for(int i0; i100; 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;BlockingQueueRunnable workQueue new LinkedBlockingQueueRunnable(10);//2:使用自定义拒绝策略入参ArrayListArrayListString 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 MyRejectedPolicyT 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 线程池详解通俗易懂超级好 线程池阻塞队列满了该怎么办线上宕机了队列里的请求会丢吗