【005】万字长文: 什么是线程池?在Java中如何使用线程池?

线程池及其在Java中的使用:

1、线程复用: 线程池是一种限制和管理线程数量的机制,可以复用线程。

2、减少开销: 减少创建和销毁线程的性能开销。

3、使用方式: 通过Executor框架中的Executors类创建,例如Executors.newFixedThreadPool(), 但是强烈建议通过构造方法创建;

4、任务提交: 将实现了RunnableCallable接口的任务提交给线程池执行。

一、什么是线程池?—— 线程的 “共享充电宝”

线程池是提前创建一批线程,统一管理、复用的 “线程容器”
核心思想是:避免频繁创建 / 销毁线程(像反复买新手机充电,不如共享充电宝随取随用),通过复用现有线程处理任务,减少资源开销,提高并发效率。

为什么需要线程池?(痛点解决)

  1. 降低资源消耗: 创建线程需要分配栈内存(默认 1M)、内核资源,销毁线程也需回收资源,频繁操作会 “浪费电”;线程池复用线程,避免这些开销。

  2. 提高响应速度: 任务来了不用等线程创建,直接从池里拿 “空闲线程”,像拿共享充电宝不用等配送。

  3. 便于管理控制: 统一控制线程数量(避免线程太多导致 CPU 飙满、内存溢出)、任务排队规则、拒绝策略(任务太多时如何处理),像充电宝柜控制 “最大借出数”“排队规则”。

二、Java 中如何使用线程池?—— 3 种核心方式(从简单到灵活)

Java 线程池的核心接口是 ExecutorService常用实现类是 ThreadPoolExecutor(推荐),还有 Executors 工具类(快速创建,但不推荐生产用)

2.1 方式1: Executors 工具类 —— 快速 “开箱即用”(入门首选)

Executors 是 JDK 提供的 “线程池工厂”,封装了常用线程池类型,一行代码就能创建。但生产环境不推荐(存在 OOM 风险,后面说原因),适合学习 / 简单场景。

示例代码:

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

public class ExecutorsDemo {
    public static void main(String[] args) {
        // 1. 固定线程数线程池(核心线程数=最大线程数,无空闲线程超时)
        // 场景:任务量稳定,避免线程频繁创建销毁(比如服务器接收请求)
        ExecutorService fixedPool = Executors.newFixedThreadPool(3); // 3个“固定工人”

        // 2. 缓存线程池(核心线程数=0,最大线程数=Integer.MAX_VALUE,空闲线程60秒销毁)
        // 场景:任务量大但耗时短(比如临时计算、异步回调)
        ExecutorService cachedPool = Executors.newCachedThreadPool(); // 无限“临时工”,60秒没活辞退

        // 3. 单线程线程池(核心线程数=最大线程数=1,任务串行执行)
        // 场景:需要任务按顺序执行(比如日志写入、数据库批量操作)
        ExecutorService singlePool = Executors.newSingleThreadExecutor(); // 1个“专属工人”

        // 提交任务(两种方式:Runnable无返回值,Callable有返回值)
        for (int i = 1; i <= 5; i++) {
            int taskNum = i;
            // 方式1:提交Runnable任务(无返回值)
            fixedPool.execute(() -> {
                System.out.println("线程" + Thread.currentThread().getName() + "处理任务" + taskNum);
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 方式2:提交Callable任务(有返回值,需用Future获取)
        /*
        Future<Integer> future = fixedPool.submit(() -> {
            return 1 + 1; // 任务返回值
        });
        try {
            System.out.println("任务结果:" + future.get()); // 阻塞获取结果
        } catch (Exception e) {
            e.printStackTrace();
        }
        */

        // 关闭线程池(必须关!否则JVM不会退出)
        fixedPool.shutdown(); // 优雅关闭:等待已提交任务完成,不再接新任务
        // fixedPool.shutdownNow(); // 强制关闭:立即中断正在执行的任务,返回未执行的任务
    }
}

运行结果(固定线程池 3 个线程处理 5 个任务):

线程pool-1-thread-1处理任务1
线程pool-1-thread-2处理任务2
线程pool-1-thread-3处理任务3
线程pool-1-thread-1处理任务4(1秒后复用)
线程pool-1-thread-2处理任务5(1秒后复用)

2.2 ThreadPoolExecutor —— 手动创建(生产推荐,灵活可控)

Executors 的底层是 ThreadPoolExecutor,但它的参数是 “硬编码” 的(比如 newCachedThreadPool 最大线程数是 Integer.MAX_VALUE,任务多了会创建大量线程导致 OOM)。生产环境推荐手动创建 ThreadPoolExecutor,按需调整核心参数。

➡️ 1. 核心参数(7大参数, 必须懂!!!)

public ThreadPoolExecutor(
    int corePoolSize,        // 核心线程数(正式工:即使空闲也不辞退,除非设置allowCoreThreadTimeOut)
    int maximumPoolSize,     // 最大线程数(正式工+临时工:线程池能容纳的最大线程数)
    long keepAliveTime,      // 空闲线程超时时间(临时工没活干多久被辞退;如果allowCoreThreadTimeOut=true,正式工也适用)
    TimeUnit unit,           // 超时时间单位(秒、毫秒等,TimeUnit.SECONDS)
    BlockingQueue<Runnable> workQueue, // 工作队列(任务排队的地方:任务来了先放队列,队列满了才创建临时工)
    ThreadFactory threadFactory,       // 线程工厂(创建线程的方式,可自定义线程名、优先级等)
    RejectedExecutionHandler handler   // 拒绝策略(任务太多:队列满了+最大线程数也用完了,如何处理任务)
)

🚀 2. 打个比方(工厂工人团队)

参数比喻例子(3 个正式工 + 2 个临时工)
corePoolSize正式工数量3 个正式工,即使没事做也不辞退
maximumPoolSize最大工人总数(正 + 临)最多 5 个工人(3 正式 + 2 临时)
keepAliveTime临时工空闲超时时间临时工没活干超过 10 秒,辞退
workQueue任务等待队列任务来了先放队列,队列满了(比如队列容量 5)才招临时工
threadFactory招工工厂给工人起名字(比如 “线程池 - 任务组 1 - 线程 1”)
handler任务拒绝策略队列满 + 工人全忙时,新任务如何处理(比如 “告诉提交者干不了”)

🎉3. 常用队列和拒绝策略

组件类型常用实现作用
工作队列ArrayBlockingQueue(有界)固定容量的队列(推荐!避免无界队列 OOM),比如 new ArrayBlockingQueue(10)
LinkedBlockingQueue(无界)无固定容量(不推荐!任务多了会 OOM),Executors.newFixedThreadPool 用它
拒绝策略AbortPolicy(默认)抛出异常 RejectedExecutionException,告诉提交者 “任务被拒绝”
CallerRunsPolicy让提交任务的线程自己执行(比如主线程提交,主线程自己处理,缓解压力)
DiscardPolicy直接丢弃任务,不抛异常(适合不重要的任务,比如日志)
DiscardOldestPolicy丢弃队列中最老的任务,再提交新任务(适合 “最新任务优先” 场景)

📌4. 生产级代码示例(自定义线程池 - 重点推荐)

import java.util.concurrent.*;

public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        // 1. 自定义线程工厂(给线程起名字,方便排查问题)
        ThreadFactory threadFactory = new ThreadFactory() {
            private int count = 1;
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("业务线程池-线程" + count++); // 自定义线程名
                thread.setDaemon(false); // 非守护线程(默认)
                return thread;
            }
        };

        // 2. 手动创建线程池(核心参数按需调整)
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            3,                      // 核心线程数:3个正式工
            5,                      // 最大线程数:5个(3正+2临)
            10,                     // 空闲超时时间:10秒
            TimeUnit.SECONDS,       // 时间单位:秒
            new ArrayBlockingQueue<>(10), // 工作队列:容量10(有界,避免OOM)
            threadFactory,          // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:提交者自己执行
        );

        // 3. 提交15个任务(3正+2临+队列10=15,刚好处理;超过15会触发拒绝策略)
        for (int i = 1; i <= 16; i++) { // 16个任务,触发拒绝策略
            int taskNum = i;
            threadPool.execute(() -> {
                System.out.println("线程" + Thread.currentThread().getName() + "处理任务" + taskNum);
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 4. 关闭线程池(优雅关闭,配合JVM钩子函数更佳)
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            threadPool.shutdown();
            System.out.println("JVM退出,线程池已优雅关闭");
        }));
    }
}

运行结果(第 16 个任务触发拒绝策略,由主线程处理):

线程业务线程池-线程1处理任务1
线程业务线程池-线程2处理任务2
线程业务线程池-线程3处理任务3
线程业务线程池-线程4处理任务4(临时工1)
线程业务线程池-线程5处理任务5(临时工2)
线程业务线程池-线程1处理任务6(1秒后复用)
...(队列中的任务陆续被处理)
线程main处理任务16(主线程自己执行,拒绝策略生效)

2.3 Spring 框架的 ThreadPoolTaskExecutor —— 企业级首选(整合 Spring 生态)

如果项目用 Spring/Spring Boot,推荐用 ThreadPoolTaskExecutor(对 ThreadPoolExecutor 的封装,支持配置化、监控等),无需手动管理线程池生命周期。

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
    // .... 
}

✅ 1. Spring Boot配置(application.yml)

spring:
  task:
    execution:
      pool:
        core-size: 3        # 核心线程数
        max-size: 5         # 最大线程数
        keep-alive: 10s     # 空闲超时时间
        queue-capacity: 10  # 队列容量
        allow-core-thread-timeout: false # 是否允许核心线程超时(默认false)
      thread-name-prefix: "spring-task-" # 线程名前缀

🎀 2. 使用(直接注入即可)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Service
public class TaskService {
    // 注入Spring管理的线程池
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    public void doTask() {
        // 提交Runnable任务(无返回值)
        taskExecutor.execute(() -> {
            System.out.println("Spring线程池处理任务:" + Thread.currentThread().getName());
        });

        // 提交Callable任务(有返回值)
        Future<Integer> future = taskExecutor.submit(() -> {
            return 100 + 200;
        });

        try {
            System.out.println("任务结果:" + future.get()); // 阻塞获取结果
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀
补充内容 - 虚拟线程结合线程池内容
🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀

三、虚拟线程 + 线程池:Java 并发的 “王炸组合”!

注意事项: JDK版本要求21, 目前Java已经到了25, 可放心使用;

如果说传统线程池是 “共享充电宝柜”(复用平台线程,减少资源开销),虚拟线程(JDK21 + 预览特性)就是 “可无限分发的一次性充电宝”(轻量级、创建成本趋近于 0)。两者结合,相当于 “充电宝柜里的每个插槽都能同时给 N 个设备充电”—— 既保留线程池的资源管控能力,又解锁虚拟线程的超高并发潜力,堪称 IO 密集场景的 “性能核弹”!

3.1 先搞懂:为啥要 “虚拟线程 + 线程池”?(单独用的痛点)

在聊结合之前,先明确单独用虚拟线程或传统线程池的 “坑”:

⬆️ 1. 单独用传统线程池(平台线程):并发上限低

传统线程池的线程是 “平台线程”(映射到操作系统内核线程),创建成本高(每个线程占 1M 栈内存)、数量有限(一般最多几百到几千个)。

比如 IO 密集场景(调用接口、查数据库),线程大部分时间在 “等响应”(阻塞),但线程池里的平台线程被占着,导致并发量上不去 —— 相当于 “充电宝柜只有 10 个插槽,10 个人在用,其他人只能排队,哪怕这 10 个人只是拿着充电宝没充电”。

✔️ 2. 单独使用虚拟线程: 资源失控风险
虚拟线程是 “用户态线程”(JVM 管理,不直接映射内核线程),创建成本极低(栈内存按需分配,可创建上百万个),但无限制创建会导致资源耗尽:

比如突然来了 100 万个请求,每个请求开一个虚拟线程,虽然虚拟线程本身轻量,但对应的任务队列、上下文切换还是会消耗 CPU 和内存 —— 相当于 “无限免费发充电宝,结果大家都堆在门口,导致通道堵塞”。

🗂️ 3. 结合的核心价值:1+1>2

  • 线程池:负责 “资源管控”(比如限制最大并发的 “载体数”、任务排队、拒绝策略),避免无限制创建虚拟线程。
  • 虚拟线程:负责 “高效执行”(IO 阻塞时自动挂起,释放载体线程给其他虚拟线程用),让线程池的 “载体” 利用率拉满。

比喻总结:线程池是 “快递站的货架”(限制最多放 10 个快递盒),虚拟线程是 “快递盒里的小包裹”(一个快递盒能装 100 个小包裹)—— 货架(线程池)控制总量,小包裹(虚拟线程)提升密度,最终快递站的处理能力暴涨!

四、底层逻辑: 虚拟线程 + 线程池的 “协作原理”

要理解结合的本质,先搞懂虚拟线程的核心特性:M:N 调度模型(M 个虚拟线程映射到 N 个平台线程)。

当虚拟线程执行 IO 操作(比如Thread.sleep()、Socket.read())阻塞时,JVM 会自动 “挂起” 这个虚拟线程,把对应的平台线程让给其他虚拟线程 —— 相当于 “你拿着充电宝没充电,快递站把充电宝暂时借给别人用,你要用时再还给你”。

而 “虚拟线程 + 线程池” 的协作流程是:

  • 线程池管理少量平台线程(比如核心线程数 = CPU 核心数 ×2),作为 “载体线程”。
  • 每个载体线程上,JVM 会调度大量虚拟线程(比如上万个)。
  • 当某个虚拟线程阻塞(IO 等待),JVM 挂起它,让载体线程执行其他虚拟线程,直到阻塞结束再恢复。

核心优势:用少量平台线程(线程池管控),承载海量虚拟线程(高并发),IO 阻塞时不浪费载体资源 —— 相当于 “10 个货架(载体线程),每个货架放 1000 个小包裹(虚拟线程),总共能处理 1 万个包裹,远超单独用货架或单独堆包裹的能力”。

在这里插入图片描述

五、Java 中如何用?(3 种实战方式,从简单到生产)

JDK21 + 提供了专门支持虚拟线程的线程池 API,结合传统ThreadPoolExecutor也能实现,下面分场景讲解:

5.1 JDK 原生 “虚拟线程池” —— 快速入门

Executors.newVirtualThreadPerTaskExecutor ()

DK21 新增的Executors静态方法,直接创建 “每个任务一个虚拟线程” 的线程池,底层是ThreadPerTaskExecutor(特殊的线程池),语法极简,适合简单场景。

代码示例(IO 密集场景:调用第三方接口)

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class VirtualThreadPoolDemo1 {
    public static void main(String[] args) throws InterruptedException {
        // 1. 创建虚拟线程池:每个任务分配一个虚拟线程
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            // 2. 提交1000个IO密集型任务(模拟调用接口,每个任务阻塞1秒)
            for (int i = 1; i <= 1000; i++) {
                int taskNum = i;
                executor.submit(() -> {
                    System.out.println("虚拟线程" + Thread.currentThread().getName() + "处理任务" + taskNum);
                    try {
                        // 模拟IO阻塞(比如调用接口、查数据库)
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        throw new RuntimeException(e);
                    }
                });
            }
        } // 3. try-with-resources自动关闭线程池,无需手动shutdown()

        System.out.println("所有任务执行完毕(总耗时≈1秒,因为1000个虚拟线程并发执行)");
    }
}

关键说明:

  • 无需手动关闭线程池:newVirtualThreadPerTaskExecutor()返回的线程池实现了AutoCloseable,try-with-resources会自动关闭。
  • 并发量爆炸:1000 个任务几乎同时执行,总耗时≈1 秒(因为虚拟线程阻塞时会释放载体线程,1000 个虚拟线程复用少量平台线程)。
  • 适用场景:简单 IO 密集任务(比如异步回调、批量接口调用),无需复杂资源管控。

5.2 手动创建 “虚拟线程池” —— 生产首选

ThreadPoolExecutor + 虚拟线程工厂

newVirtualThreadPerTaskExecutor()是 “黑盒”,无法自定义核心参数(比如队列容量、拒绝策略)。

生产环境推荐用ThreadPoolExecutor手动创建,通过Thread.ofVirtual().factory()指定虚拟线程工厂,灵活管控资源。

核心参数设计(IO 密集场景):

  • 核心线程数 / 最大线程数:建议设为CPU核心数×2(比如 8 核 CPU 设为 16),因为虚拟线程阻塞时会释放载体线程,不需要太多平台线程。
  • 工作队列:用有界队列(比如ArrayBlockingQueue),避免任务过多导致 OOM。
  • 线程工厂:Thread.ofVirtual().name(“biz-vt-”, 1).factory()(创建虚拟线程,自定义线程名)。
  • 拒绝策略:根据业务选择(比如CallerRunsPolicy缓解压力)。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class VirtualThreadPoolDemo2 {
    public static void main(String[] args) throws InterruptedException {
        // 1. 虚拟线程工厂(自定义线程名,方便排查问题)
        var threadFactory = Thread.ofVirtual()
                .name("order-vt-", 1) // 线程名前缀:order-vt-1、order-vt-2...
                .factory();

        // 2. 手动创建虚拟线程池(核心参数按需调整)
        var threadPool = new ThreadPoolExecutor(
            16,                      // 核心线程数:16(8核CPU×2)
            16,                      // 最大线程数:16(IO密集场景,核心=最大,避免创建临时工)
            0,                       // 空闲超时时间:0(虚拟线程轻量,无需销毁)
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000), // 有界队列:容量1000(避免OOM)
            threadFactory,           // 虚拟线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:提交者自己执行
        );

        // 3. 提交2000个IO密集任务(队列1000+线程16=1016,超过1016触发拒绝策略)
        for (int i = 1; i <= 2000; i++) {
            int taskNum = i;
            threadPool.submit(() -> {
                System.out.println("线程" + Thread.currentThread().getName() + "处理订单任务" + taskNum);
                try {
                    // 模拟IO阻塞(比如查询订单数据库、调用支付接口)
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(e);
                }
            });
        }

        // 4. 优雅关闭线程池(配合JVM钩子函数)
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            threadPool.shutdown();
            try {
                if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
                    threadPool.shutdownNow(); // 5秒后未关闭,强制关闭
                }
                System.out.println("应用退出,虚拟线程池已关闭");
            } catch (InterruptedException e) {
                threadPool.shutdownNow();
            }
        }));

        // 等待任务执行(实际生产中无需手动等待,由应用生命周期管理)
        threadPool.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println("所有订单任务执行完毕");
    }
}

关键说明:

  • 重点阅读代码里的注释文字. 写的非常清楚了.

运行效果:

  • 16 个平台线程(载体)承载 2000 个虚拟线程任务,总耗时≈(2000/16)×0.5 秒≈62.5 秒(如果用传统线程池,2000 个任务需要排队,总耗时≈2000×0.5 秒 = 1000 秒,差距巨大!)。
  • 超过 1016 个任务时,触发拒绝策略,由提交线程(主线程)执行,避免任务丢失。

5.3 Spring Boot 整合虚拟线程池(企业级实战)

如果项目用 Spring Boot 3.2+(支持 JDK21+),可以直接通过配置启用虚拟线程池,无需手动创建,整合更丝滑。

✅ 1. 配置文件

spring:
  task:
    execution:
      pool:
        core-size: 16          # 核心线程数(平台线程数,IO密集=CPU×2)
        max-size: 16           # 最大线程数(和核心一致,避免临时工)
        keep-alive: 0s         # 空闲超时时间(虚拟线程无需销毁)
        queue-capacity: 1000   # 有界队列容量
        thread-name-prefix: "spring-vt-" # 虚拟线程名前缀
      virtual:
        enabled: true          # 启用虚拟线程池(关键!)

✔️ 2. 使用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class OrderService {
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    // 异步处理订单(返回CompletableFuture,支持异步回调)
    public CompletableFuture<Void> processOrder(int orderId) {
        return CompletableFuture.runAsync(() -> {
            System.out.println("虚拟线程" + Thread.currentThread().getName() + "处理订单" + orderId);
            try {
                // 模拟IO阻塞(调用支付接口、扣减库存)
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("订单处理失败", e);
            }
        }, taskExecutor);
    }
}

控制器调用(Web 场景)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    // 接口并发测试:同时调用1000次,无压力
    @GetMapping("/order/{id}")
    public CompletableFuture<String> processOrder(@PathVariable int id) {
        return orderService.processOrder(id)
                .thenApply(v -> "订单" + id + "处理成功");
    }
}

关键优势:

  • Spring 自动管理虚拟线程池生命周期,无需手动关闭。
  • 支持@Async注解(直接在方法上加@Async,无需手动提交任务)。
  • 适配 Spring 生态(比如结合CompletableFuture、Spring Cloud异步调用)。

六、核心注意点(避坑指南,重中之重!)

6.1 虚拟线程只适合 “IO 密集型” 任务,不适合 “计算密集型”

  • IO 密集型(推荐):调用接口、查数据库、文件 IO 等(线程大部分时间阻塞,虚拟线程能释放载体线程,提升并发)。
  • 计算密集型(不推荐):数学计算、循环处理等(线程一直占用 CPU,虚拟线程挂起机制无用,反而增加 JVM 调度开销,不如用传统线程池 + CPU 核心数线程)。

6.2 线程池参数不能乱设(IO 密集场景最优配置)

参数配置建议原因
核心线程数 / 最大线程数CPU 核心数 ×2(比如 8 核 = 16)虚拟线程阻塞时释放载体,无需太多平台线程,多了反而增加上下文切换开销
工作队列有界队列(比如ArrayBlockingQueue(1000)无界队列会导致任务无限堆积,触发 OOM
空闲超时时间0 秒(或极短时间)虚拟线程轻量,无需销毁,保留载体线程复用即可
拒绝策略CallerRunsPolicy或自定义策略虚拟线程并发高,拒绝策略需避免任务丢失,同时缓解系统压力

6.3 避免 “虚拟线程嵌套虚拟线程”

比如在一个虚拟线程里再创建一个虚拟线程池,会导致 JVM 调度复杂,性能下降 —— 相当于 “在一个小包裹里再塞 100 个小包裹,货架管理混乱”

6.4 监控虚拟线程状态(生产必备)

虚拟线程的状态可以通过Thread的 API 获取,结合监控工具(Prometheus+Grafana)告警:

// 获取线程池状态
System.out.println("活跃虚拟线程数:" + threadPool.getActiveCount());
System.out.println("队列等待任务数:" + threadPool.getQueue().size());
System.out.println("已完成任务数:" + threadPool.getCompletedTaskCount());

七、总结

  1. 线程池是 “线程复用容器”,核心优势是降低资源消耗、提高响应速度、便于管理。
  2. Java 使用线程池的 3 种方式:
  • Executors:快速入门,不推荐生产用(OOM 风险)。
  • ThreadPoolExecutor:手动创建,灵活可控,生产首选。
  • ThreadPoolTaskExecutor:Spring 生态专用,企业级开发常用。
  1. 核心原则:用有界队列、合理设置线程数、选对拒绝策略、必须关闭线程池。
  2. 虚拟线程 + 线程池,IO 密集场景的 “性能天花板”
    • 核心逻辑:线程池管 “载体”(平台线程),虚拟线程管 “任务”(高并发),利用 M:N 调度模型让 IO 阻塞时不浪费资源。
    • 适用场景:接口调用、数据库操作、消息消费等 IO 密集型场景(并发量提升 10 倍 +,耗时大幅降低)。
    • 不适用场景:纯计算任务(比如数据加密、复杂算法),不如传统线程池高效。

用一句热梗收尾:传统线程池是 “自行车队”(能拉货但慢),虚拟线程是 “电动车”(快但续航有限),两者结合是 “高铁”—— 又快又稳,还能拉满乘客(任务)!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值