Java 线程池详细解析及实战案例(推荐Executors)

文章目录

前言

线程池是 Java 并发编程中的一个重要概念,它通过复用已创建的线程来减少线程创建和销毁的开销,提高应用程序的性能。本文将详细介绍 Java 线程池的工作原理、常用配置,并结合实战案例展示其应用。

线程池的基本概念

1.什么是线程池?

线程池是一种多线程处理形式,处理过程中将任务添加到队列中,并在线程空闲时执行任务。线程池的核心思想是预先创建并维护一定数量的线程,避免频繁创建和销毁线程带来的资源消耗。

2. 线程池的主要组件

核心线程数(Core Pool Size):线程池中保持的最小线程数,即使这些线程处于空闲状态。

最大线程数(Maximum Pool Size):线程池中允许的最大线程数。

工作队列(Work Queue):用于保存等待执行的任务队列。

线程工厂(Thread Factory):用于创建新线程的工厂类,默认使用 Executors.defaultThreadFactory()。

拒绝策略(Rejected Execution Handler):当线程池无法处理新任务时的处理策略。

Java 线程池的实现类

Java 提供了多种线程池实现类,最常用的是 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。此外,Executors 工具类提供了便捷的静态方法来创建不同类型的线程池。

1、ThreadPoolExecutor

ThreadPoolExecutor 是 Java 中最常用的线程池实现类,提供了丰富的配置选项。

构造函数

bash 复制代码
public ThreadPoolExecutor(
    int corePoolSize, //核心线程数
    int maximumPoolSize, //最大线程数
    long keepAliveTime, //线程空闲时间,超过此时间的空闲线程将被终止
    TimeUnit unit, //keepAliveTime 的时间单位
    BlockingQueue<Runnable> workQueue, //任务队列
    ThreadFactory threadFactory, //线程工厂
    RejectedExecutionHandler handler) //拒绝策略

2、Executors 工具类

Executors 提供了一些静态方法来简化线程池的创建:

newFixedThreadPool(int nThreads):创建一个固定大小的线程池。

newCachedThreadPool():创建一个根据需要创建新线程的线程池。

newSingleThreadExecutor():创建一个单线程的线程池。

newScheduledThreadPool(int corePoolSize):创建一个支持定时和周期性任务执行的线程池。

线程池的配置与调优

1、核心线程数和最大线程数

核心线程数:应根据 CPU 核心数和任务类型设置。对于 CPU 密集型任务,核心线程数可以设置为 CPU 核心数;对于 I/O 密集型任务,可以适当增加。

最大线程数:应根据系统资源和任务负载动态调整,避免过多线程导致上下文切换频繁。

2、工作队

常见的工作队列类型包括:

SynchronousQueue:不存储元素的阻塞队列,直接将任务交给线程处理。

LinkedBlockingQueue:基于链表的有界或无界阻塞队列。

ArrayBlockingQueue:基于数组的有界阻塞队列。

PriorityBlockingQueue:支持优先级排序的无界阻塞队列。

3、 拒绝策略

常见的拒绝策略包括:

AbortPolicy:抛出 RejectedExecutionException 异常。

CallerRunsPolicy:由调用线程执行任务。

DiscardPolicy:直接丢弃任务。

DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试重新提交当前任务。

实战案例

案例 1:批量处理文件上传

假设我们有一个 Web 应用程序,用户可以批量上传多个文件。为了提高处理速度,我们可以使用线程池来并发处理文件上传请求。

示例代码

java 复制代码
/**
 * FileUploadService
 * @author senfel
 * @version 1.0
 * @date 2025/1/3 14:52
 */
@Service
public class FileUploadService {
    private final ExecutorService executor = new ThreadPoolExecutor(
            4, // 核心线程数
            10, // 最大线程数
            60L, TimeUnit.SECONDS, // 空闲线程存活时间
            new LinkedBlockingQueue<>(100), // 工作队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
    );

    /**
     * uploadFiles
     * @param files
     * @author senfel
     * @date 2025/1/3 14:54 
     * @return void
     */
    public void uploadFiles(List<File> files) {
        for (File file : files) {
            executor.submit(() -> processFile(file));
        }
    }

    private void processFile(File file) {
        try {
            // 模拟文件处理逻辑
            System.out.println("Processing file: " + file.getName());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * shutdown
     * @author senfel
     * @date 2025/1/3 14:54 
     * @return void
     */
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

案例 2:定时任务调度

假设我们需要定期清理过期的日志文件。可以使用 ScheduledThreadPoolExecutor 来实现定时任务调度。

示例代码

java 复制代码
/**
 * LogCleanupService
 * @author senfel
 * @version 1.0
 * @date 2025/1/3 14:55
 */
@Service
public class LogCleanupService {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    /**
     * startLogCleanup
     * @author senfel
     * @date 2025/1/3 14:56 
     * @return void
     */
    public void startLogCleanup() {
        Runnable cleanupTask = () -> {
            try {
                // 模拟日志清理逻辑
                System.out.println("Cleaning up logs...");
                Thread.sleep(5000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        };

        // 每隔 1 小时执行一次日志清理任务
        scheduler.scheduleAtFixedRate(cleanupTask, 0, 1, TimeUnit.HOURS);
    }

    /**
     * shutdown
     * @author senfel
     * @date 2025/1/3 14:57 
     * @return void
     */
    public void shutdown() {
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
        }
    }
}

案例 3:异步任务处理

假设我们有一个订单处理系统,需要异步处理订单通知。可以使用 CompletableFuture 结合线程池来实现异步任务处理。

示例代码

java 复制代码
/**
 * OrderNotificationService
 * @author senfel
 * @version 1.0
 * @date 2025/1/3 14:57
 */
@Service
public class OrderNotificationService {
    private final ExecutorService executor = Executors.newFixedThreadPool(5);

    /**
     * sendNotification
     * @param orderId
     * @author senfel
     * @date 2025/1/3 14:59
     * @return java.util.concurrent.CompletableFuture<java.lang.Void>
     */
    public CompletableFuture<Void> sendNotification(Long orderId) {
        return CompletableFuture.runAsync(() -> {
            try {
                // 模拟发送通知逻辑
                System.out.println("Sending notification for order: " + orderId);
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, executor);
    }

    /**
     * shutdown
     * @author senfel
     * @date 2025/1/3 14:59
     * @return void
     */
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

总结

方案 优点 确定 适用场景
ThreadPoolExecutor 配置灵活,功能强大; 需要手动管理线程池生命周期 适用于复杂任务调度
Executors 工具类使用简单,快速创建线程池; 默认配置可能不适合所有场景 适用于快速开发
ScheduledThreadPoolExecutor 支持定时和周期性任务; 功能相对单一 适用于定时任务调度
CompletableFuture 异步编程模型,易于组合; 学习曲线较陡 适用于异步任务处理

在实际的编码场景中,我们通过合理配置和使用线程池,可以显著提升 Java 应用程序的性能和响应速度。

相关推荐
南宫生13 分钟前
力扣-数据结构-12【算法学习day.83】
java·数据结构·学习·算法·leetcode
KeyPan19 分钟前
【数据结构与算法:五、树和二叉树】
java·开发语言·数据结构·人工智能·算法·机器学习·计算机视觉
工业甲酰苯胺20 分钟前
Java Web学生自习管理系统
java·开发语言·前端
Fre丸子_23 分钟前
ffmpeg之yuv格式转h264
开发语言·qt·ffmpeg
晚上睡不着!27 分钟前
Java程序命令行调用Python矩阵算法
java·开发语言·python·numpy
哥屋恩052830 分钟前
C#中鼠标点击获取Chart图形上的坐标值
开发语言·c#
_周游43 分钟前
【C语言】_野指针
c语言·开发语言
青木川崎43 分钟前
java进阶之maven
java·开发语言·maven
拾荒的小海螺1 小时前
JAVA:Spring Boot 集成 Quartz 实现分布式任务的技术指南
java·spring boot·分布式
ghostwritten1 小时前
Conda 安装 Jupyter Notebook
开发语言·ide·python·jupyter·conda