线程池讲解-----Executors 附带生产级代码案例

7 阅读8分钟

 Java 自带的线程池(Executors 工具类)

Java 通过 Executors 工具类提供了5 种常用的自带线程池,底层都是基于 ThreadPoolExecutor 实现的,简化了线程池的创建。

注意:阿里开发手册不建议直接使用 Executors 创建线程池(部分实现存在 OOM 风险),但作为基础知识点必须掌握,生产环境推荐手动定义 ThreadPoolExecutor

一、5 种内置线程池详解

  1. newFixedThreadPool 固定大小线程池
  • 核心特点:核心线程数 = 最大线程数
  • 使用队列  LinkedBlockingQueue 无界队列
  • 适用场景控制最大并发数,负载稳定的任务
  • 特性:线程数量固定,空闲线程不会被回收,队列无限制
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolDemo {
    public static void main(String[] args) {
        // 创建固定 3 个线程的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交 10 个任务
        for (int i = 1; i <= 10; i++) {
            int taskNum = i;
            executor.execute(() -> {
                System.out.println("任务 " + taskNum + " 被线程 " + Thread.currentThread().getName() + " 执行");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}


  1. newCachedThreadPool 缓存线程池
  • 核心特点:无核心线程,最大线程数**Integer.MAX_VALUE**,自动回收空闲线程  
  • 队列类型  SynchronousQueue(不存任务,来一个任务必须立刻开线程  最大线程21 亿)
  • 适用场景大量短生命周期、轻量级的异步任务
  • 特性:来任务就创建线程,空闲 60 秒自动销毁,任务密集时会无限创建线程(OOM 风险


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 1; i <= 10; i++) {
            int taskNum = i;
            executor.execute(() -> {
                System.out.println("任务 " + taskNum + " 被线程 " + Thread.currentThread().getName() + " 执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}


  1. newSingleThreadExecutor 单线程线程池
  • 核心特点:只有 1 个核心线程
  • 使用队列  LinkedBlockingQueue 无界队列
  • 适用场景任务串行执行,保证顺序
  • 特性:所有任务按提交顺序执行,线程异常会自动重建
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        for (int i = 1; i <= 5; i++) {
            int taskNum = i;
            executor.execute(() -> {
                System.out.println("任务 " + taskNum + " 被线程 " + Thread.currentThread().getName() + " 执行");
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}


  1. newScheduledThreadPool 定时 / 周期线程池
  • 核心特点:支持延迟执行、周期性执行任务
  • 使用队列 DelayedWorkQueue 延迟队列
  • 适用场景定时任务、周期任务(如定时报表、定时清理)
  • 特性:基于 ScheduledExecutorService,核心线程固定,非核心线程无限->OOM
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

        // 1. 延迟 2 秒执行一次
        executor.schedule(() -> {
            System.out.println("延迟任务执行:" + System.currentTimeMillis());
        }, 2, TimeUnit.SECONDS);

        // 2. 初始延迟 1 秒,之后每 3 秒执行一次(固定频率)
        executor.scheduleAtFixedRate(() -> {
            System.out.println("周期任务执行:" + System.currentTimeMillis());
        }, 1, 3, TimeUnit.SECONDS);
    }
}


  1. newWorkStealingPool 抢占式线程池(JDK8+)
  • 核心特点:基于ForkJoinPool,多任务队列,工作窃取算法
  • 使用队列 WorkStealingQueue(工作窃取队列) ,本质是 ForkJoinPool 的内部队列机制
  • 队列特点:
  • 双端队列(Deque) :每个线程都有自己的任务队列
  • 窃取机制:当自己的队列任务干完了,会去 “偷” 别的线程队列里的任务(通常是偷尾巴,减少竞争)
  • 适用场景CPU 密集型任务,任务之间相互独立
  • 特性:默认线程数 = CPU 核心数,并行效率高,异步处理能力强
  • 缺点: 不适合 IO 密集型任务(网络、数据库、文件)一个 IO 阻塞会拖慢所有线程,不能保证任务执行顺序,不能手动设置队列大小
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WorkStealingPoolDemo {
    public static void main(String[] args) {
        // 无参:默认线程数 = CPU 核心数
        ExecutorService executor = Executors.newWorkStealingPool();

        for (int i = 1; i <= 10; i++) {
            int taskNum = i;
            executor.execute(() -> {
                System.out.println("任务 " + taskNum + " 被线程 " + Thread.currentThread().getName() + " 执行");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // workStealingPool 是守护线程,需要阻塞等待
        try {
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

二 生产的线程池封装

1常量配置(建议放 application.yml 里)

public class ThreadPoolConstant {

    // CPU核心数
    public static final int CPU_CORE = Runtime.getRuntime().availableProcessors();

    // IO密集型:核心线程 = CPU * 2
    public static final int CORE_POOL_SIZE = CPU_CORE * 2;

    // 最大线程
    public static final int MAX_POOL_SIZE = CPU_CORE * 4;

    // 空闲线程存活时间
    public static final long KEEP_ALIVE_SECONDS = 60L;

    // 队列容量(必须有界!)
    public static final int QUEUE_CAPACITY = 2048;

    // 线程名前缀(非常重要)
    public static final String THREAD_NAME_PREFIX = "biz-async-task-";
}

自定义线程工厂(带命名、优先级、守护线程)

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class BizThreadFactory implements ThreadFactory {

    private final AtomicInteger threadSeq = new AtomicInteger(1);
    private final String threadNamePrefix;

    public BizThreadFactory(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        // 设置线程名
        thread.setName(threadNamePrefix + threadSeq.getAndIncrement());
        // 非守护线程,确保任务执行完
        thread.setDaemon(false);
        // 正常优先级
        thread.setPriority(Thread.NORM_PRIORITY);

        // 异常捕获(防止线程池里任务异常导致线程消失)
        thread.setUncaughtExceptionHandler((t, e) -> {
            log.error("线程 [{}] 执行任务异常", t.getName(), e);
        });

        log.info("创建业务线程: {}", thread.getName());
        return thread;
    }
}

自定义拒绝策略(记录日志 + 降级处理)

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class BizRejectedPolicy implements ThreadPool.RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 打印关键监控信息
        log.error(
                "线程池拒绝任务!" +
                        " 活跃线程数:{}" +
                        " 队列大小:{}" +
                        " 已完成任务数:{}",
                executor.getActiveCount(),
                executor.getQueue().size(),
                executor.getCompletedTaskCount()
        );

        // 核心业务拒绝策略:让调用者同步执行(不丢任务)
        if (!executor.isShutdown()) {
            log.warn("触发CallerRuns降级策略,由主线程同步执行任务");
            r.run();
        }
    }
}

线程池单例封装

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.*;

@Component
@Slf4j
public class BizAsyncThreadPool {

    @Getter
    private ThreadPoolExecutor executor;

    @PostConstruct
    public void init() {
        log.info("===== 初始化业务异步线程池开始 =====");

        // 1. 队列:有界队列,防止OOM
        BlockingQueue<Runnable> workQueue =
                new ArrayBlockingQueue<>(ThreadPoolConstant.QUEUE_CAPACITY);

        // 2. 线程工厂
        ThreadFactory threadFactory =
                new BizThreadFactory(ThreadPoolConstant.THREAD_NAME_PREFIX);

        // 3. 拒绝策略
        RejectedExecutionHandler rejectedHandler = new BizRejectedPolicy();

        // 4. 创建线程池
        executor = new ThreadPoolExecutor(
                ThreadPoolConstant.CORE_POOL_SIZE,
                ThreadPoolConstant.MAX_POOL_SIZE,
                ThreadPoolConstant.KEEP_ALIVE_SECONDS,
                TimeUnit.SECONDS,
                workQueue,
                threadFactory,
                rejectedHandler
        );

        // 允许核心线程超时回收(空闲时释放资源)
        executor.allowCoreThreadTimeOut(true);

        log.info("===== 初始化业务异步线程池完成 =====");
        log.info("核心线程数:{},最大线程数:{},队列容量:{}",
                executor.getCorePoolSize(),
                executor.getMaximumPoolSize(),
                workQueue.size()
        );
    }

    // ====================== 提交任务 ======================

    /**
     * 无返回值任务
     */
    public void execute(Runnable task) {
        executor.execute(task);
    }

    /**
     * 带返回值任务
     */
    public <T> Future<T> submit(Callable<T> task) {
        return executor.submit(task);
    }

    // ====================== 优雅停机 ======================
    @PreDestroy
    public void shutdown() {
        log.info("===== 开始关闭业务异步线程池 =====");

        // 拒绝新任务
        executor.shutdown();

        try {
            // 等待60秒,让现有任务执行完
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                log.warn("线程池关闭超时,强制关闭");
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            log.error("线程池关闭被中断,强制关闭");
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }

        log.info("===== 业务异步线程池已关闭 =====");
    }
}

业务使用示例Service 中

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@Slf4j
public class TestController {

    private final BizAsyncThreadPool bizAsyncThreadPool;

    @GetMapping("/test/async")
    public String testAsync() {
        log.info("主线程开始处理请求");

        // 提交异步任务:发送短信
        bizAsyncThreadPool.execute(() -> {
            try {
                // 模拟调用第三方短信接口
                log.info("【异步任务】开始发送短信...");
                Thread.sleep(1000);
                log.info("【异步任务】短信发送成功");
            } catch (InterruptedException e) {
                log.error("发送短信异常", e);
                Thread.currentThread().interrupt();//sleep()wait()join()遇到会抛出InterruptedException 异常
            }
        });

        // 提交异步任务:记录日志
        bizAsyncThreadPool.execute(() -> {
            try {
                log.info("【异步任务】记录操作日志");
                Thread.sleep(500);
                log.info("【异步任务】日志落库成功");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        log.info("主线程立即返回");
        return "success";
    }
}


生产级定时任务线程池

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class ScheduledTaskPool {

    @Getter
    private ScheduledThreadPoolExecutor executor;

    @PostConstruct
    public void init() {
        // 核心线程固定2~4个足够
        executor = new ScheduledThreadPoolExecutor(2,
                new BizThreadFactory("biz-scheduled-task-")
        );

        // 关键!限制最大线程,防止无限创建
        executor.setMaximumPoolSize(4);
        executor.setRejectedExecutionHandler(new BizRejectedPolicy());
        executor.setKeepAliveTime(60L, TimeUnit.SECONDS);
        executor.allowCoreThreadTimeOut(true);

        log.info("定时任务线程池初始化完成");
    }

    /**
     * 固定频率执行
     */
    public void scheduleAtFixedRate(Runnable task, long initialDelay, long period)//任务  初始延迟   周期
 {
        executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);
    }

    /**
     * 固定间隔执行
     */
    public void scheduleWithFixedDelay(Runnable task, long initialDelay, long delay)//任务  初始延迟   间隔
 {
        executor.scheduleWithFixedDelay(task, initialDelay, delay, TimeUnit.SECONDS);
    }

    @PreDestroy
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

三 线程池基础知识

ThreadPoolExecutor 7 大参数 + 队列 + 拒绝策略

线程池构造方法

真正的线程池只有这一个:

new ThreadPoolExecutor(
    int corePoolSize,        // 1. 核心线程数
    int maximumPoolSize,     // 2. 最大线程数
    long keepAliveTime,      // 3. 空闲线程存活时间
    TimeUnit unit,           // 4. 时间单位
    BlockingQueue<Runnable> workQueue,  // 5. 任务队列
    ThreadFactory threadFactory,        // 6. 线程工厂
    RejectedExecutionHandler handler    // 7. 拒绝策略
);

7 大参数系统讲解

  1. corePoolSize 核心线程数
  • 线程池中长期保留的常驻线程
  • 即使空闲,默认也不会被销毁
  • IO 密集型:设置为 CPU 核心数 × 2 (IO等待为主 实际不消耗)
  • CPU 密集型:设置为 CPU 核心数 + 1(计算为主 减少线程切换上下文消耗)
  1. maximumPoolSize 最大线程数
  • 线程池最多能创建多少线程
  • 队列满了,才会创建新线程,直到达到这个数
  • 不能无限大,否则 OOM
  1. keepAliveTime 空闲线程存活时间
  • 超过核心线程数的线程,空闲多久会被回收
  • 一般设 60 秒
  1. unit 时间单位
  • 秒、毫秒等
  • 配合 keepAliveTime 使用
  1. workQueue 任务队列(重点)

任务进来,先放队列,队列满了才开新线程。

常用 3 种:

  1. ArrayBlockingQueue

    • 有界队列
    • 生产环境必须用这个
    • 可以设置容量,防止 OOM
  2. LinkedBlockingQueue

    • 无界队列(默认容量 Integer.MAX_VALUE)
    • Executors 固定线程池用这个
    • 任务无限堆积 → OOM
  3. SynchronousQueue

    • 不存储任务,来一个任务必须立刻开线程
    • 缓存线程池用这个
    • 高并发会疯狂创建线程 → OOM
  4. threadFactory 线程工厂

  • 用来创建线程

  • 生产必须自定义:

    • 设置线程名(排查问题必备)
    • 设置是否守护线程(后台辅助线程,只要所有用户线程都结束,JVM 直接杀死所有守护线程)
    • 设置异常捕获
  1. handler 拒绝策略(重点)

当:线程数达到 maximumPoolSize并且队列已满新任务进来就会触发拒绝策略。


三、4 种拒绝策略(系统整理)

  1. AbortPolicy(默认)
  • 直接抛出异常 RejectedExecutionException
  • 阻止任务提交
  • 生产不推荐,会直接报错
  1. CallerRunsPolicy(生产最推荐)
  • 让提交任务的主线程自己执行这个任务
  • 不会丢任务
  • 不会抛异常
  • 压力大时能自动限流、保护系统
  1. DiscardPolicy
  • 直接丢弃任务,不抛异常
  • 业务不能丢任务时绝对不能用
  1. DiscardOldestPolicy
  • 丢弃队列里最老的任务
  • 再尝试提交当前任务
  • 可能导致关键任务丢失,慎用