Java多线程全体系教程 - 第三篇:Java多线程高并发实战·JUC工具与企业规范篇

5 阅读6分钟

Java多线程全体系教程 - 第三篇:Java多线程高并发实战·JUC工具与企业规范篇

适合人群:已经掌握线程安全与锁机制,需要学习企业级线程池、JUC并发工具、解决死锁、落地高并发规范的开发者

核心定位:完全贴合企业生产环境规范,告别手动创建线程,掌握生产级线程池、JUC工具类、死锁排查与解决,落地可直接上线的高并发代码。

一、企业级线程池:ThreadPoolExecutor(生产唯一规范)

线程池是企业开发中多线程的唯一落地方式,核心作用:复用线程、控制线程数量、统一管理线程生命周期、避免频繁创建销毁线程带来的资源消耗

1.1 为什么禁止使用Executors创建线程池?

Executors工具类提供的快捷创建方法,底层都是ThreadPoolExecutor,但默认参数存在严重隐患:

  1. newFixedThreadPool、newSingleThreadExecutor:使用无界LinkedBlockingQueue,队列最大长度Integer.MAX_VALUE,高并发下会堆积大量任务,导致OOM内存溢出;

  2. 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. 拒绝策略:任务队列满、线程数达到最大,执行拒绝策略
)

核心参数详解
  1. corePoolSize:核心线程数,线程池创建后,默认没有线程,任务提交后创建核心线程,核心线程会一直存活,即使空闲也不会销毁;

  2. maximumPoolSize:线程池最大线程数,核心线程都忙碌、队列满了之后,才会创建非核心线程,总线程数不超过最大线程数;

  3. keepAliveTime:非核心线程空闲超过这个时间,就会被销毁,释放资源;

  4. workQueue:阻塞队列,存储等待执行的任务,生产环境必须使用有界队列,禁止使用无界队列;

  5. threadFactory:自定义线程工厂,给线程设置有意义的名称,方便后续问题排查;

  6. handler:拒绝策略,线程池满了之后,如何处理新提交的任务。

1.3 线程池执行任务的完整流程(必考)

  1. 提交任务,判断核心线程数是否已满,未满则创建核心线程执行任务;

  2. 核心线程已满,判断阻塞队列是否已满,未满则将任务放入队列等待;

  3. 阻塞队列已满,判断当前线程数是否达到最大线程数,未满则创建非核心线程执行任务;

  4. 达到最大线程数,执行拒绝策略,拒绝新提交的任务。

1.4 4种内置拒绝策略

  1. AbortPolicy(默认):直接抛出RejectedExecutionException异常,阻止程序运行,生产环境不推荐;

  2. CallerRunsPolicy:任务回退给提交任务的主线程执行,不会丢失任务,不会抛出异常,生产环境常用;

  3. DiscardPolicy:直接丢弃当前任务,不抛出异常,业务允许丢失任务时使用;

  4. 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