用代码示例说明如何通过线程池实现Java多线程编程

简介: Java线程池通过复用线程提升多线程效率,减少资源开销。本文详解ExecutorService、ThreadPoolExecutor等核心API,结合Runnable与Callable任务示例,演示线程池创建、任务提交、结果获取及关闭流程,并强调自定义线程池的必要性与最佳实践。

在Java中,线程池是管理多线程的高效方式,它通过复用线程减少频繁创建/销毁线程的开销。以下通过代码示例详细说明如何使用线程池实现多线程编程,包括线程池的创建、提交任务、获取结果及关闭线程池等核心操作。

一、线程池核心API简介

Java通过java.util.concurrent包提供线程池支持,核心类和接口:

  • ExecutorService:线程池核心接口,定义了提交任务、关闭线程池等方法。
  • Executors:工具类,提供快速创建常见线程池的静态方法(实际开发中推荐ThreadPoolExecutor自定义线程池,更灵活)。
  • ThreadPoolExecutor:线程池的核心实现类,可自定义核心线程数、最大线程数等参数。

二、线程池使用步骤(代码示例)

1. 基本使用:提交Runnable任务(无返回值)

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

public class ThreadPoolRunnableDemo {
   
    public static void main(String[] args) {
   
        // 1. 创建线程池(这里用Executors.newFixedThreadPool创建固定大小的线程池)
        // 核心线程数为3,即同时最多运行3个线程
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 2. 提交任务(Runnable接口,无返回值)
        for (int i = 0; i < 5; i++) {
    // 提交5个任务
            final int taskId = i; // 任务编号
            // 提交Runnable任务
            executor.submit(new Runnable() {
   
                @Override
                public void run() {
   
                    // 任务逻辑:打印线程名和任务ID,模拟耗时操作
                    System.out.println("线程" + Thread.currentThread().getName() 
                                       + " 执行任务" + taskId);
                    try {
   
                        Thread.sleep(1000); // 模拟任务耗时1秒
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                }
            });
        }

        // 3. 关闭线程池(重要!否则程序会一直运行)
        // shutdown():不再接受新任务,等待已有任务执行完毕后关闭
        executor.shutdown();
    }
}

输出说明:
线程池有3个核心线程,5个任务会分两批执行(前3个同时执行,1秒后执行剩余2个),输出类似:

线程pool-1-thread-1 执行任务0
线程pool-1-thread-2 执行任务1
线程pool-1-thread-3 执行任务2
(1秒后)
线程pool-1-thread-1 执行任务3
线程pool-1-thread-2 执行任务4

2. 提交Callable任务(有返回值)

如果需要线程返回结果,可提交Callable任务,通过Future获取结果。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolCallableDemo {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        // 1. 创建线程池(单线程池,仅1个线程)
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 2. 提交Callable任务(有返回值,这里返回1~n的和)
        Callable<Integer> task = new Callable<Integer>() {
   
            @Override
            public Integer call() throws Exception {
   
                int sum = 0;
                for (int i = 1; i <= 100; i++) {
   
                    sum += i;
                }
                return sum; // 返回计算结果
            }
        };

        // 提交任务并获取Future对象(用于获取结果)
        Future<Integer> future = executor.submit(task);

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

        // 4. 通过Future获取结果(get()会阻塞,直到任务完成)
        int result = future.get();
        System.out.println("1~100的和:" + result); // 输出:5050
    }
}

多任务批量获取结果示例:

public class ThreadPoolMultiCallableDemo {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        // 创建固定大小为2的线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 存储多个Future对象
        List<Future<Integer>> futures = new ArrayList<>();

        // 提交3个任务(计算1~n的和)
        for (int i = 1; i <= 3; i++) {
   
            final int num = i * 100; // 分别计算1~100、1~200、1~300的和
            Future<Integer> future = executor.submit(new Callable<Integer>() {
   
                @Override
                public Integer call() throws Exception {
   
                    int sum = 0;
                    for (int j = 1; j <= num; j++) {
   
                        sum += j;
                    }
                    return sum;
                }
            });
            futures.add(future);
        }

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

        // 遍历Future获取所有结果
        for (int i = 0; i < futures.size(); i++) {
   
            System.out.println("1~" + (i + 1) * 100 + "的和:" + futures.get(i).get());
        }
    }
}

输出:

1~100的和:5050
1~200的和:20100
1~300的和:45150

3. 自定义线程池(ThreadPoolExecutor)

Executors创建的线程池有潜在风险(如newCachedThreadPool可能创建大量线程导致OOM),实际开发中推荐用ThreadPoolExecutor自定义参数:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPoolDemo {
   
    public static void main(String[] args) {
   
        // 自定义线程池参数
        int corePoolSize = 2; // 核心线程数(常驻线程)
        int maximumPoolSize = 4; // 最大线程数(核心线程+临时线程)
        long keepAliveTime = 60; // 临时线程空闲时间
        TimeUnit unit = TimeUnit.SECONDS; // 时间单位
        // 任务队列(容量为2,超出的任务会创建临时线程)
        ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);

        // 创建自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue
        );

        // 提交5个任务
        for (int i = 0; i < 5; i++) {
   
            final int taskId = i;
            executor.submit(new Runnable() {
   
                @Override
                public void run() {
   
                    System.out.println("线程" + Thread.currentThread().getName() 
                                       + " 执行任务" + taskId);
                    try {
   
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                }
            });
        }

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

参数说明:

  • 核心线程数2,任务队列容量2。提交5个任务时:
    • 前2个任务由核心线程执行;
    • 中间2个任务放入队列等待;
    • 第5个任务触发创建临时线程(最大线程数4,因此可创建2个临时线程)。

三、线程池常用方法

方法 说明
submit(Runnable) 提交无返回值的任务,返回Future<?>
submit(Callable<T>) 提交有返回值的任务,返回Future<T>
shutdown() 平缓关闭:不再接受新任务,等待现有任务完成后关闭
shutdownNow() 强制关闭:尝试中断所有任务,返回未执行的任务
isShutdown() 判断线程池是否已关闭
isTerminated() 判断所有任务是否执行完毕

四、线程池优势总结

  1. 复用线程:减少线程创建/销毁的资源消耗。
  2. 控制并发数:避免线程过多导致的系统资源耗尽。
  3. 管理任务队列:有序执行任务,避免任务丢失。
  4. 提高响应速度:核心线程常驻,无需等待线程创建即可执行任务。

通过线程池实现多线程编程是工业级开发的最佳实践,需根据业务场景合理选择线程池类型和参数。

相关文章
|
存储 Java 调度
Springboot集成Quartz(任务存储在数据库)
集成quartz实现定时调度,quartz是一个功能丰富的开源的任务调用系统,它可以定义很多job并发执行,支持事务和集群
2178 0
|
10月前
|
机器学习/深度学习 传感器 监控
基于 YOLOv8 的智能火灾识别系统设计与实现— 从数据集训练到 PyQt5 可视化部署的完整工程实践
本项目设计并实现了一款基于YOLOv8的智能火灾识别系统,融合深度学习与计算机视觉技术,支持图片、视频、摄像头等多源输入。采用PyQt5开发图形界面,具备高精度、实时性强、易部署等优点,适用于智慧消防、工业巡检等场景,提供完整代码与模型权重,真正实现开箱即用。
785 5
基于 YOLOv8 的智能火灾识别系统设计与实现— 从数据集训练到 PyQt5 可视化部署的完整工程实践
|
消息中间件
RabbitMQ-如何保证消息不丢失
在RabbitMQ中,可以通过以下几种方式来保证消息不丢失:
1115 0
|
SQL XML JavaScript
【若依Java】15分钟玩转若依二次开发,新手小白半小时实现前后端分离项目,springboot+vue3+Element Plus+vite实现Java项目和管理后台网站功能
摘要: 本文档详细介绍了如何使用若依框架快速搭建一个基于SpringBoot和Vue3的前后端分离的Java管理后台。教程涵盖了技术点、准备工作、启动项目、自动生成代码、数据库配置、菜单管理、代码下载和导入、自定义主题样式、代码生成、启动Vue3项目、修改代码、以及对代码进行自定义和扩展,例如单表和主子表的代码生成、树形表的实现、商品列表和分类列表的改造等。整个过程详细地指导了如何从下载项目到配置数据库,再到生成Java和Vue3代码,最后实现前后端的运行和功能定制。此外,还提供了关于软件安装、环境变量配置和代码自动生成的注意事项。
32748 73
|
12月前
|
JavaScript 安全 Java
基于springboot的大学生兼职系统
本课题针对大学生兼职信息不对称、权益难保障等问题,研究基于Spring Boot、Vue、MySQL等技术的兼职系统,旨在构建安全、高效、功能完善的平台,提升大学生就业竞争力与兼职质量。
|
Java 程序员
Java社招面试中的高频考点:Callable、Future与FutureTask详解
大家好,我是小米。本文主要讲解Java多线程编程中的三个重要概念:Callable、Future和FutureTask。它们在实际开发中帮助我们更灵活、高效地处理多线程任务,尤其适合社招面试场景。通过 Callable 可以定义有返回值且可能抛出异常的任务;Future 用于获取任务结果并提供取消和检查状态的功能;FutureTask 则结合了两者的优势,既可执行任务又可获取结果。掌握这些知识不仅能提升你的编程能力,还能让你在面试中脱颖而出。文中结合实例详细介绍了这三个概念的使用方法及其区别与联系。希望对大家有所帮助!
939 60
|
JavaScript 前端开发
vue实现侧边折叠菜单栏手风琴效果
该文章介绍了如何使用Vue.js实现具有手风琴效果的侧边折叠菜单栏,包括HTML结构设计、CSS样式设置以及JavaScript交互逻辑的编写。
|
缓存 关系型数据库 MySQL
一文彻底弄懂MySQL优化之深度分页
【10月更文挑战第24天】本文深入探讨了 MySQL 深度分页的原理、常见问题及优化策略。首先解释了深度分页的概念及其带来的性能和资源问题。接着介绍了基于偏移量(OFFSET)和限制(LIMIT)以及基于游标的分页方法,并分析了它们的优缺点。最后,提出了多种优化策略,包括合理创建索引、优化查询语句和使用数据缓存,帮助提升分页查询的性能和系统稳定性。
2207 1
|
JSON 前端开发 Java
获取HTTP请求参数的方法
获取HTTP请求参数的方法
|
敏捷开发 测试技术 持续交付
阿里云云效产品使用合集之如何确保解决冲突代码是提交到合并的目标分支
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。