线程池-----Executors

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

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

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

一、5 种内置线程池详解

  1. **newFixedThreadPool**固定大小线程池
  • 核心特点:核心线程数 = 最大线程数

  • 使用队列 LinkedBlockingQueue 无界队列

  • 适用场景控制最大并发数,负载稳定的任务

  • 特性:线程数量固定,空闲线程不会被回收,队列无限制

java 复制代码
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 风险

java 复制代码
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 无界队列

  • 适用场景任务串行执行,保证顺序

  • 特性:所有任务按提交顺序执行,线程异常会自动重建

java 复制代码
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();
    }
}

4.newScheduledThreadPool 定时 / 周期线程池

  • 核心特点:支持延迟执行、周期性执行任务

  • 使用队列 DelayedWorkQueue 延迟队列

  • 适用场景定时任务、周期任务(如定时报表、定时清理)

  • 特性 :基于 ScheduledExecutorService,核心线程固定,非核心线程无限->OOM

java 复制代码
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 阻塞会拖慢所有线程,不能保证任务执行顺序,不能手动设置队列大小

java 复制代码
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 里)

java 复制代码
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-";
}

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

java 复制代码
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;
    }
}

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

java 复制代码
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();
        }
    }
}

线程池单例封装

java 复制代码
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 中

java 复制代码
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";
    }
}

生产级定时任务线程池

java 复制代码
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 大参数 + 队列 + 拒绝策略

线程池构造方法

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

java 复制代码
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
  • 丢弃队列里最老的任务

  • 再尝试提交当前任务

  • 可能导致关键任务丢失,慎用

相关推荐
小碗羊肉2 小时前
【从零开始学Java | 第二十八篇】可变参数
java·开发语言
Java成神之路-2 小时前
Spring AOP 核心进阶:切入点表达式 + 通知类型 + 环绕通知避坑指南(Spring系列8)
java·后端·spring
weitingfu2 小时前
Excel VBA 入门到精通(二):变量、数据类型与运算符
java·大数据·开发语言·学习·microsoft·excel·vba
foundbug9992 小时前
无人机离散系统模型预测控制(MPC)MATLAB实现
开发语言·matlab·无人机
某人辛木2 小时前
Maven一步到位
java·maven
爱写代码的小朋友2 小时前
使用 Nuitka 打包 Python 应用:从入门到进阶
开发语言·python
一条咸鱼_SaltyFish2 小时前
DDD 架构重构实践:AI Skills 如何赋能DDD设计与重构
java·人工智能·ai·重构·架构·ddd·领域驱动设计
yuan199972 小时前
C# 断点续传下载文件工具设计与实现
开发语言·c#
想唱rap2 小时前
线程之条件变量和生产消费模型
java·服务器·开发语言·数据库·mysql·ubuntu