Java 线程池(一)

Java 线程池技术笔记

一、线程池基础

1. 什么是线程池

程序启动时创建固定数量线程存入池中待命,任务到来取出空闲线程执行,任务完成线程不销毁,回归池中等候新任务。

2. 线程池优点

  • 降低资源消耗:避免频繁创建/销毁线程
  • 提高响应速度:线程已存在,无需等待创建
  • 提高可管理性:统一分配、监控、调优
  • 防止资源耗尽:限制并发线程数,避免系统崩溃

3. Java 线程池核心组件

  • 核心接口:ExecutorService
  • 常用实现:ThreadPoolExecutor // 生产环境推荐使用
  • 工具类:Executors // 提供快捷创建方式,生产慎用

二、ThreadPoolExecutor 核心参数

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,          // 核心线程数,常驻不销毁
    int maximumPoolSize,       // 最大线程数,包含核心+临时
    long keepAliveTime,        // 空闲线程存活时间
    TimeUnit unit,             // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务阻塞队列
    ThreadFactory threadFactory,       // 线程工厂,自定义线程名/属性
    RejectedExecutionHandler handler   // 拒绝策略
)

执行逻辑(重点)

  1. 线程数 < corePoolSize → 新建核心线程执行
  2. 线程数 ≥ corePoolSize → 任务入队列
  3. 队列满 且 线程数 < maximumPoolSize → 新建临时线程
  4. 队列满 且 线程数 = maximumPoolSize → 触发拒绝策略

三、标准使用代码示例

java 复制代码
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 1. 自定义线程池参数
        int corePoolSize = 2;
        int maxPoolSize = 4;
        long keepAliveTime = 10;
        // 任务队列容量2
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);

        // 自定义线程工厂 // 方便日志定位问题
        ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r, "my-pool-thread-" + threadNumber.getAndIncrement());
                t.setDaemon(false); // 非守护线程,保证任务执行完
                return t;
            }
        };

        // 拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        // 创建线程池
        ExecutorService executor = new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                workQueue,
                threadFactory,
                handler
        );

        // 2. 提交10个任务
        for (int i = 1; i <= 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                String threadName = Thread.currentThread().getName();
                System.out.printf("[%s] 开始执行任务 %d%n", threadName, taskId);
                try {
                    TimeUnit.SECONDS.sleep(1); // 模拟业务执行
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 恢复中断位
                }
                System.out.printf("[%s] 完成任务 %d%n", threadName, taskId);
            });
        }

        // 3. 关闭线程池 // 必须关闭,否则进程不退出
        executor.shutdown();
        try {
            // 等待最多30秒
            if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                executor.shutdownNow(); // 超时强制关闭
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        System.out.println("所有任务执行完毕,线程池已关闭");
    }
}

四、Executors 快捷线程池

java 复制代码
// 固定线程数 // 队列无界,生产可能OOM
ExecutorService fixedPool = Executors.newFixedThreadPool(5);

// 单线程线程池 // 保证顺序执行
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 按需创建线程 // 线程数无界,高并发风险
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 定时/周期任务
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);

定时任务示例

java 复制代码
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 1秒后开始,每3秒执行一次
scheduler.scheduleAtFixedRate(() -> {
    System.out.println("heartbeat");
}, 1, 3, TimeUnit.SECONDS);

scheduler.shutdown();

五、拒绝策略

  • AbortPolicy():抛出异常,默认策略
  • CallerRunsPolicy():调用者线程执行 // 温和限流
  • DiscardPolicy():直接丢弃,不抛异常
  • DiscardOldestPolicy():丢弃队列最老任务,重试提交

六、线程池监控(常用方法)

java 复制代码
ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;
pool.getPoolSize();          // 当前线程总数
pool.getActiveCount();       // 活跃线程数
pool.getQueue().size();      // 队列等待任务数
pool.getCompletedTaskCount();// 已完成任务数

七、生产注意事项

  1. 必须关闭线程池

    非守护线程会导致进程无法退出,用 shutdown() / shutdownNow()

  2. 任务必须捕获异常

    任务未捕获异常会导致线程被销毁,线程池重建线程,推荐任务内 try-catch

  3. 禁止滥用 Executors

    生产直接用 ThreadPoolExecutor,避免队列/线程数无界导致OOM。

  4. 合理设置参数

    CPU密集型:核心线程数 ≈ CPU核心数

    IO密集型:核心线程数 ≈ 2*CPU核心数


八、配套实战案例(敏感词检测)

java 复制代码
import java.util.Random;
import java.util.concurrent.*;

// 网络服务类,持续生成任务提交线程池
class NetworkService implements Runnable {
    final ThreadPoolExecutor pool;

    public NetworkService(int poolSize) {
        // 自定义线程池,生产标准写法
        this.pool = new ThreadPoolExecutor(
                poolSize,
                30,
                3000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(100)
        );
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        Random random = new Random();
        while (true) {
            // 生成随机字符串
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 20; i++) {
                sb.append((char) (random.nextInt(126 - 32) + 32));
            }
            // 提交任务
            pool.execute(new Handler(sb.toString()));

            // 运行100秒后关闭线程池
            if (System.currentTimeMillis() - start > 100000) {
                pool.shutdown();
                break;
            }
        }
    }
}

// 任务处理器:检测是否包含敏感词a
class Handler implements Runnable {
    private final String str;

    public Handler(String str) {
        this.str = str;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000); // 模拟处理耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        if (str.contains("a")) {
            System.out.println(str + ": 包含敏感词");
        } else {
            System.out.println(str + ": 不包含敏感词");
        }
    }
}

// 主类
class Main {
    public static void main(String[] args) {
        NetworkService service = new NetworkService(10);
        new Thread(service).start();

        // 监控线程池状态
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    break;
                }
                ThreadPoolExecutor pool = service.pool;
                System.out.println("ActiveCount: " + pool.getActiveCount());
                System.out.println("PoolSize: " + pool.getPoolSize());
                System.out.println("TaskCount: " + pool.getTaskCount());
            }
        }).start();
    }
}
相关推荐
eggrall2 小时前
Linux进程信号——像收快递一样理解 Linux 信号
linux·开发语言·c++
Full Stack Developme2 小时前
spring-beans 解析
java·后端·spring
foundbug9992 小时前
MATLAB实现:基于图像对比度和波段相关性的高光谱波段选择算法
开发语言·算法·matlab
码农-阿杰2 小时前
生成偏向锁 + JIT
java
czt_java2 小时前
线程安全问题
java·开发语言·jvm
likerhood2 小时前
设计模式-装饰器模式(java)
java·设计模式·装饰器模式
爱学习的小可爱卢2 小时前
Java抽象类与接口:面试高频考点全解析
java·javase
techdashen2 小时前
Rust 模块和文件不是一回事:一次讲清 `mod`、`use`、`pub use`
开发语言·后端·rust
Wy_编程2 小时前
go中的协程Goroutine
开发语言·golang