Java多线程全体系教程 - 第三篇:Java多线程高并发实战·JUC工具与企业规范篇
适合人群:已经掌握线程安全与锁机制,需要学习企业级线程池、JUC并发工具、解决死锁、落地高并发规范的开发者
核心定位:完全贴合企业生产环境规范,告别手动创建线程,掌握生产级线程池、JUC工具类、死锁排查与解决,落地可直接上线的高并发代码。
一、企业级线程池:ThreadPoolExecutor(生产唯一规范)
线程池是企业开发中多线程的唯一落地方式,核心作用:复用线程、控制线程数量、统一管理线程生命周期、避免频繁创建销毁线程带来的资源消耗。
1.1 为什么禁止使用Executors创建线程池?
Executors工具类提供的快捷创建方法,底层都是ThreadPoolExecutor,但默认参数存在严重隐患:
-
newFixedThreadPool、newSingleThreadExecutor:使用无界LinkedBlockingQueue,队列最大长度Integer.MAX_VALUE,高并发下会堆积大量任务,导致OOM内存溢出;
-
newCachedThreadPool、newScheduledThreadPool:最大线程数是Integer.MAX_VALUE,高并发下会创建大量线程,导致OOM内存溢出。
企业开发强制规范:必须手动通过ThreadPoolExecutor构造方法创建线程池,自定义所有参数,规避OOM风险。
1.2 ThreadPoolExecutor 7个核心参数(必考)
// 标准构造方法
public ThreadPoolExecutor(
int corePoolSize, // 1. 核心线程数:线程池长期存活的常驻线程
int maximumPoolSize, // 2. 最大线程数:线程池能创建的最大线程数量
long keepAliveTime, // 3. 非核心线程空闲存活时间
TimeUnit unit, // 4. 存活时间单位
BlockingQueue<Runnable> workQueue, // 5. 阻塞队列:存储等待执行的任务
ThreadFactory threadFactory, // 6. 线程工厂:自定义创建线程,设置线程名、优先级
RejectedExecutionHandler handler // 7. 拒绝策略:任务队列满、线程数达到最大,执行拒绝策略
)
核心参数详解
-
corePoolSize:核心线程数,线程池创建后,默认没有线程,任务提交后创建核心线程,核心线程会一直存活,即使空闲也不会销毁;
-
maximumPoolSize:线程池最大线程数,核心线程都忙碌、队列满了之后,才会创建非核心线程,总线程数不超过最大线程数;
-
keepAliveTime:非核心线程空闲超过这个时间,就会被销毁,释放资源;
-
workQueue:阻塞队列,存储等待执行的任务,生产环境必须使用有界队列,禁止使用无界队列;
-
threadFactory:自定义线程工厂,给线程设置有意义的名称,方便后续问题排查;
-
handler:拒绝策略,线程池满了之后,如何处理新提交的任务。
1.3 线程池执行任务的完整流程(必考)
-
提交任务,判断核心线程数是否已满,未满则创建核心线程执行任务;
-
核心线程已满,判断阻塞队列是否已满,未满则将任务放入队列等待;
-
阻塞队列已满,判断当前线程数是否达到最大线程数,未满则创建非核心线程执行任务;
-
达到最大线程数,执行拒绝策略,拒绝新提交的任务。
1.4 4种内置拒绝策略
-
AbortPolicy(默认):直接抛出RejectedExecutionException异常,阻止程序运行,生产环境不推荐;
-
CallerRunsPolicy:任务回退给提交任务的主线程执行,不会丢失任务,不会抛出异常,生产环境常用;
-
DiscardPolicy:直接丢弃当前任务,不抛出异常,业务允许丢失任务时使用;
-
DiscardOldestPolicy:丢弃队列中等待最久的任务,将当前任务加入队列,生产环境慎用。
1.5 生产环境标准线程池代码(可直接复制使用)
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
/**
* 生产环境标准线程池配置(可直接复用)
*/
public class StandardThreadPool {
// 核心线程数:默认设置为 CPU核心数 + 1
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
// 最大线程数:IO密集型设置为 2*CPU核心数,计算密集型设置为 CPU核心数
private static final int MAX_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
// 非核心线程空闲存活时间
private static final long KEEP_ALIVE_TIME = 60L;
// 阻塞队列容量:有界队列,容量100~500,根据业务调整,绝对禁止无界队列
private static final int QUEUE_CAPACITY = 200;
// 单例线程池
private static volatile ThreadPoolExecutor threadPool;
// 私有化构造方法
private StandardThreadPool() {}
// 双重校验锁,获取单例线程池
public static ThreadPoolExecutor getThreadPool() {
if (threadPool == null) {
synchronized (StandardThreadPool.class) {
if (threadPool == null) {
// 自定义线程工厂,设置线程名称,方便排查问题
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("custom-business-thread-%d")
.setDaemon(false)
.build();
threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
// 有界数组队列,性能最高
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
threadFactory,
// 拒绝策略:调用者执行,不丢失任务
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
}
return threadPool;
}
// 优雅关闭线程池
public static void shutdownThreadPool() {
if (threadPool != null) {
threadPool.shutdown();
// 等待线程池执行完毕,最多等待30秒
try {
if (!threadPool.awaitTermination(30, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
}
}
}
}
核心注意事项:
1. 线程池必须设置为单例,全局唯一,禁止一个业务创建一个线程池;
2. 阻塞队列必须使用有界队列(ArrayBlockingQueue),绝对禁止使用无界队列;
3. 必须自定义线程工厂,设置线程名称,方便线上问题排查;
4. 拒绝策略优先使用CallerRunsPolicy,避免任务丢失。
二、JUC并发工具类(高并发必备)
JUC(java.util.concurrent)包,是Java官方提供的高并发工具包,封装了大量开箱即用的并发工具类,解决复杂的线程同步、通信问题,不用手动写锁和wait/notify。
2.1 CountDownLatch:倒计时门闩,等待多个线程执行完毕
作用:让一个线程等待,其他多个线程都执行完成后,这个线程才继续执行。
经典场景:主线程等待所有子线程加载完数据,再汇总结果。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 创建CountDownLatch,设置计数为3,等待3个线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(3);
ThreadPoolExecutor threadPool = StandardThreadPool.getThreadPool();
// 提交3个任务
for (int i = 1; i <= 3; i++) {
int taskNum = i;
threadPool.submit(() -> {
try {
System.out.println(Thread.currentThread().getName() + "执行任务" + taskNum);
} finally {
// 每个线程执行完毕,计数减1
countDownLatch.countDown();
System.out.println("当前剩余计数:" + countDownLatch.getCount());
}
});
}
// 主线程阻塞
``` Java多线程全体系教程 - 第三篇:Java多线程高并发实战·JUC工具与企业规范篇 **适合人群**:已经掌握线程安全与锁机制,需要学习企业级线程池、J