当前位置: 首页 > article >正文

Java线程池详解

线程池是用来管理和复用线程的一种技术,它避免了频繁的创建和销毁线程的开销,提高了应用程序的性能。在 Java 中,ExecutorService 是一个非常常用的接口,它提供了线程池的基本功能。

在这里插入图片描述

1. 线程池的优势

  • 线程复用:线程池管理一组固定数量的线程,任务提交时可以复用空闲线程,避免了每个任务都创建新线程的开销。
  • 减少资源消耗:线程池中有固定数量的线程,避免了频繁创建和销毁线程导致的性能损耗。
  • 控制并发数:线程池可以限制并发线程的数量,防止线程数量过多造成系统资源紧张。

2. Java 线程池的基本使用

Java 提供了 Executors 工厂类来创建常用的线程池类型。

2.1 创建线程池

常见的线程池类型:

  • 固定大小线程池:Executors.newFixedThreadPool(int nThreads)
  • 缓存线程池:Executors.newCachedThreadPool()
  • 单线程池:Executors.newSingleThreadExecutor()
  • 调度线程池:Executors.newScheduledThreadPool(int corePoolSize)
2.2 示例代码
import java.util.concurrent.*;

public class ThreadPoolExample {

    public static void main(String[] args) {
        // 创建一个固定大小为 3 的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交任务
        for (int i = 0; i < 5; i++) {
            executor.submit(new Task(i));
        }

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

    static class Task implements Runnable {
        private int taskId;

        public Task(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            try {
                System.out.println("Task " + taskId + " is running on thread: " + Thread.currentThread().getName());
                // 模拟任务执行
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}
2.3 线程池的工作原理
  1. 提交任务:通过 executor.submit() 提交任务到线程池。
  2. 线程池工作:线程池中的线程会从任务队列中获取任务,并执行。
  3. 任务执行完成:执行完成后,线程会空闲,等待新的任务。
  4. 关闭线程池:调用 executor.shutdown() 可以优雅地关闭线程池,待所有任务完成后关闭。

3. 常用线程池方法

  • submit():提交一个任务并返回一个 Future 对象,可以通过 Future 获取任务执行的结果。
  • invokeAll():提交多个任务,并等待所有任务完成,返回一个 List<Future>。
  • invokeAny():提交多个任务,返回第一个执行完成的任务结果。
  • shutdown():优雅地关闭线程池,不再接收新任务,但会执行已经提交的任务。
  • shutdownNow():立即停止线程池,返回当前在执行的任务。

4. 使用 Future 获取任务结果

Future 对象代表异步计算的结果。可以通过 get() 方法获取计算结果,get() 方法会阻塞直到任务完成。

ExecutorService executor = Executors.newFixedThreadPool(2);
Future future = executor.submit(() -&gt; {
    // 执行任务并返回结果
    return 123;
});

try {
    Integer result = future.get(); // 阻塞,直到任务完成
    System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
executor.shutdown();

5. 线程池的最佳实践

  • 合理选择线程池大小:根据系统硬件配置和任务的性质来选择线程池的大小。一般来说,可以使用 CPU 核心数 * 2 作为线程池大小的参考。
  • 避免使用 Executors 静态工厂方法:Executors 的工厂方法有些存在问题,比如 newFixedThreadPool(0) 会创建一个无限大小的线程池,因此可以使用 ThreadPoolExecutor 来灵活控制线程池。
  • 手动配置 ThreadPoolExecutor:通过 ThreadPoolExecutor,你可以精细控制线程池的参数。
ExecutorService executor = new ThreadPoolExecutor(
    4,  // corePoolSize
    10, // maximumPoolSize
    60L, // keepAliveTime
    TimeUnit.SECONDS, // unit
    new LinkedBlockingQueue&lt;&gt;(), // workQueue
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

总结

线程池是一个非常重要的并发编程工具,可以有效管理和复用线程,避免资源浪费,提高性能。在使用时,可以根据具体的需求选择合适的线程池类型,并掌握一些线程池的配置和优化技巧。


http://www.kler.cn/a/405098.html

相关文章:

  • 主要用于图像的颜色提取、替换以及区域修改
  • 【论文速读】| RobustKV:通过键值对驱逐防御大语言模型免受越狱攻击
  • Python虚拟环境入门:虚拟环境如何工作、如何自定义创建和管理管理工具venv、Virtualenv、conda
  • Linux驱动开发(9):pinctrl子系统和gpio子系统--led实验
  • 什麼是ISP提供的公共IP地址?
  • IDEA2019搭建Springboot项目基于java1.8 解决Spring Initializr无法创建jdk1.8项目 注释乱码
  • 基于单片机中医药柜管理系统的设计
  • P1048 [NOIP2005 普及组] 采药
  • Redis中的zset用法详解
  • Redis-monitor安装与配置
  • AJAX的基本使用
  • 【Redis】基于Redis实现秒杀功能
  • Java list
  • uni-app 界面TabBar中间大图标设置的两种方法
  • CentOs7静态IP地址配置方法
  • 低音运行,约克VRF中央空调让居家生活静享安宁
  • C++小白实习日记——Day 1 怎么跑github上下载的程序
  • Mybatis框架之代理模式 (Proxy Pattern)
  • Redis三剑客:缓存雪崩、缓存穿透、缓存击穿
  • 国标GB28181设备管理软件EasyGBS国标GB28181视频平台:RTMP和GB28181两种视频上云协议的区别
  • RNN简单理解;为什么出现Transformer:传统RNN的问题;Attention(注意力机制)和Self-Attention(自注意力机制)区别;
  • SQLAlchemy,ORM的Python标杆!
  • 嵌入式硬件电子电路设计(六)LDO低压差线性稳压器全面详解
  • 音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
  • 开源许可协议
  • 【Swift】字符串和字符