1. 概述
1.1 线程池的特点
线程池 (Thread Pool) 是一种基于池化思想帮助我们管理线程而获取并发性的工具,经常出现在多线程服务器中,如 MySQL。线程池的实现思路:提前创建好多个线程,让这些线程处于就绪状态来提高系统响应速度,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用。
内存池 (Memory Pooling):预先申请内存,提升申请内存速度,减少内存碎片。
连接池 (Connection Pooling):预先申请数据库连接,提升申请连接的速度,降低系统的开销。
实例池 (Object Pooling):循环使用对象,减少资源在初始化和释放时的昂贵损耗。
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。(线程复用)
- 提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会因为线程的不合理分布,导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。(控制最大并发数)
- 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池 ScheduledThreadPoolExecutor 允许任务延期执行或定期执行。
1.2 异步回调
- 同步和异步通常用来形容一次方法调用。
- 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
- 异步方法调用一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而异步方法通常会在另一个线程中“真实”地执行。整个过程,不会阻碍调用者的工作。对于调用者来说,异步调用似乎是一瞬间完成的。如果异步调用需要返回结果,那么当这个异步调用真实完成时,会通知调用者。
package com.java.forkjoinpool;
import java.util.concurrent.CompletableFuture;
/**
* @author rrqstart
* @Description 异步回调
* public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {//......}
*/
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
//异步调用,无返回值,开启一个新线程来执行任务
//public static CompletableFuture<Void> runAsync(Runnable runnable)
CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName() + ":无返回值。");
});
Void v = completableFuture1.get();
System.out.println(v); //null
System.out.println("----------------------------------------");
//异步回调,有返回值,开启一个新线程来执行任务
//public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + ":有返回值。");
// int i = 10 / 0;
return 1024;
});
Integer i = completableFuture2.whenComplete((t, u) -> {
System.out.println("t = " + t);
System.out.println("u = " + u);
}).exceptionally(f -> {
System.out.println("exception : " + f.getMessage());
return 404;
}).get();
System.out.println(i);
}
}
//int i = 10 / 0;被注释掉时程序的输出结果:
ForkJoinPool.commonPool-worker-9:无返回值。
null
----------------------------------------
ForkJoinPool.commonPool-worker-9:有返回值。
t = 1024
u = null
1024
//int i = 10 / 0;没有被注释掉时程序的输出结果:
ForkJoinPool.commonPool-worker-9:无返回值。
null
----------------------------------------
ForkJoinPool.commonPool-worker-9:有返回值。
t = null
u = java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
exception : java.lang.ArithmeticException: / by zero
404
2. Executor框架
2.1 Executor框架简介
- 在 Java 中,使用线程来异步的执行任务。Java 的线程既是工作单元,也是执行机制。从 JDK1.5 开始,把工作单元与执行机制分离开来,工作单元包括 Runnable 和 Callable,执行机制由 Executor 框架提供。
- Executor 框架的两级调度模型:
- 在 HotSpot VM 的线程模型中,Java 线程被一对一映射为本地操作系统的线程。当 Java 线程启动时会创建一个本地操作系统线程;当该 Java 线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将它们分配给可用的CPU。
- 在上层,Java 多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor 框架)将这些任务映射为固定数量的线程;在底层,操作系统内核(OS Kernel)将这些线程映射到硬件处理器上。
- Executor 框架的结构:
- 任务:包括被执行任务需要实现的接口 Runnable 和 Callable。
- 任务的执行:包括任务执行机制的核心接口 Executor,以及继承自 Executor 的 ExecutorService 接口。
- 异步计算的结果:包括接口 Future 和实现 Future 的 FutureTask 类。
- ExecutorService 是真正的线程池接口,常见子类是 ThreadPoolExecutor。
2.2 Executors源码分析
package java.util.concurrent;
//Since:JDK1.5
public class Executors {
//1.创建一个只有一个线程的线程池:一个任务一个任务的执行,适用于需要保证顺序的执行各个任务。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
//2.创建一个固定线程数的可重用的线程池:执行长期任务性能好,适合严格限制线程数的场景,如:负载比较重的服务器。
public static ExecutorService newFixedThreadPool(int nThreads) {
//使用无界队列LinkedBlockingQueue(队列容量为:Integer.MAX_VALUE)作为线程池的工作队列
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
//3.创建一个线程数可伸缩的线程池:适用于执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重启它们。可扩容!
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
/**
* ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。
* 表示在给定的延迟之后执行任务,或者定期执行任务。
* ScheduledThreadPoolExecutor的功能与Timer类似,但其功能更强大、更灵活。
* Timer对应的是单个后台线程,ScheduledThreadPoolExecutor可以对应1个或多个后台线程。
*/
//4.创建固定数量线程的线程池:适合多个后台线程执行周期任务,同时为了满足资源管理的需求而限制后台线程数量的场景。
public static ScheduledExecutorService newScheduledThreadPool(int