线程池有几种创建方式?

2023-11-12

总体来说线程池的创建可以分为以下两类:

  • 通过 ThreadPoolExecutor 手动创建线程池
  • 通过 Executors 执行器自动创建线程池。
    在这里插入图片描述

而以上两类创建线程池的方式,又有 7 种具体实现方法,这 7 种实现方法分别是:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。

接下来我们分别来看这 7 种线程池的具体使用。

1、newFixedThreadPool

  • 创建一个固定大小的线程池,可控制并发线程数。使用 FixedThreadPool 创建 2 个固定大小的线程池
  • 具体实现代码如下:
public static void fixedThreadPool() {
    // 创建 2 个线程的线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    // 创建任务
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
        }
    };

    // 线程池执行任务(一次添加 4 个任务)
    // 执行任务的方法有两种:submit 和 execute
    threadPool.submit(runnable);  // 执行方式 1:submit
    threadPool.execute(runnable); // 执行方式 2:execute
    threadPool.execute(runnable);
    threadPool.execute(runnable);
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • 如果觉得以上方法比较繁琐,还用使用以下简单的方式来实现线程池的创建和使用:
public static void fixedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    // 执行任务
    threadPool.execute(() -> {
        System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
    });
}

2、newCachedThreadPool

  • 创建一个可缓存的线程池,若线程数超过任务所需,那么多余的线程会被缓存一段时间后才被回收,若线程数不够,则会新建线程。
  • CachedThreadPool 使用示例如下:
public static void cachedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newCachedThreadPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        threadPool.execute(() -> {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • 从上述结果可以看出,线程池创建了 10 个线程来执行相应的任务。
  • 使用场景:CachedThreadPool 是根据短时间的任务量来决定创建的线程数量的,所以它适合短时间内有突发大量任务的处理场景。

3、newSingleThreadExecutor

  • 创建单个线程的线程池,它可以保证先进先出的执行顺序。
  • SingleThreadExecutor 使用示例如下:
public static void singleThreadExecutor() {
    // 创建线程池
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + ":任务被执行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述

  • 单个线程的线程池有什么意义?

  • 单个线程的线程池相比于线程来说,它的优点有以下 2 个:

  • 可以复用线程:即使是单个线程池,也可以复用线程。

  • 提供了任务管理功能:单个线程池也拥有任务队列,在任务队列可以存储多个任务,这是线程无法实现的,并且当任务队列满了之后,可以执行拒绝策略,这些都是线程不具备的。

4、newScheduledThreadPool

  • 创建一个可以执行延迟任务的线程池。
  • 使用示例如下:
public static void scheduledThreadPool() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    // 添加定时执行任务(1s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 1, TimeUnit.SECONDS);
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • 从上述结果可以看出,任务在 1 秒之后被执行了,实现了延迟 1s 再执行任务。

5、newSingleThreadScheduledExecutor

  • 创建一个单线程的可以执行延迟任务的线程池,此线程池可以看作是 ScheduledThreadPool 的单线程池版本。
  • 它的使用示例如下:
public static void SingleThreadScheduledExecutor() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // 添加定时执行任务(2s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • 从上述结果可以看出,任务在 2 秒之后被执行了。

6、newWorkStealingPool

  • 创建一个抢占式执行的线程池(任务执行顺序不确定),此方法是 JDK 1.8 版本新增的,因此只有在 JDK 1.8 以上的程序中才能使用。
  • newWorkStealingPool 使用示例如下:
public static void workStealingPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
        });
    }
    // 确保任务执行完成
    while (!threadPool.isTerminated()) {
    }
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • 从上述结果可以看出,任务的执行顺序是不确定的,因为它是抢占式执行的。

7、ThreadPoolExecutor

  • ThreadPoolExecutor 是最原始、也是最推荐的手动创建线程池的方式,它在创建时最多提供 7 个参数可供设置。
  • ThreadPoolExecutor 使用示例如下:
public static void myThreadPoolExecutor() {
    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}
  • 以上程序的执行结果如下图所示:
    在这里插入图片描述
  • ThreadPoolExecutor 相比于其他创建线程池的优势在于,它可以通过参数来控制最大任务数和拒绝策略,让线程池的执行更加透明和可控,所以在阿里巴巴《Java开发手册》是这样规定的:
  • 【强制要求】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
  • 说明:Executors 返回的线程池对象的弊端如下:
    1、FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    2、CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

总结

线程池的创建方式总共有以下 7 种:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。
  • 而线程池的创建推荐使用最后一种 ThreadPoolExecutor 的方式来创建,因为使用它可以明确线程池的运行规则,规避资源耗尽的风险。

详细链接https://blog.csdn.net/HongYu012/article/details/123331122

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程池有几种创建方式? 的相关文章

随机推荐

  • postman接口传参案例

    目录 案例1 接口A 接口B 案例2 断言 案例1 接口A 根据返回值需要从返回值中提取userid值 在Tests标签栏下编写脚本 获取返回的响应值 并转化为json格式 var jsonData pm response json 获取返
  • Springboot中管理Spring容器重写工具类进行使用

    说明 SpringUtils 既spring工具类 方便在非spring管理环境中获取bean 在SpringBoot或者SpringMVC框架中 基于Spring进行管理容器以及上下文或者等前置操作等 因此需要实现 BeanFactory
  • Fiddler抓包工具之fiddler设置手机端抓包

    fiddler设置手机端抓包 安卓手机抓包 第一步 配置电脑和安卓的相关设置 1 手机和fiddler位于同一个局域网内 首先从fiddler处获取到ip地址和端口号 点击online 最后一行就是ip地址 2 路径 Tools Optio
  • uni-app跨端开发微信小程序之手把手带你写一个用程序自动打开微信开发者工具的小插件

    摘要 本文通过获取微信开发者工具安装路径 调用shelljs执行vue cli编译命令 fs和path组合来读取编译后的目录 动态修改AppId和title这四个方面入手 一步步带领看官制作一个自动打开微信开发者工具的小插件 完美解决日常多
  • Typora设置修改字体颜色快捷键

    目录 1 typora如何设置修改字体颜色快捷键 2 AutoHotKey软件安装 3 typora关于AutoHotKey的具体操作 1 typora如何设置修改字体颜色快捷键 typora本身是不能直接修改字体颜色的 不过若是想修改还是
  • buck和boost电路

    文章目录 buck和boost电路 1 占空比计算 2 电感值计算 buck和boost电路 归属于DCDC非隔离电源的一部分 最常用的拓扑方式 1 占空比计算 电感两端电压与电流关系 V L d i
  • Windows下编译VTK-9.1.0

    VTK 9编译要点 VTK 9 1 0 src CMake vtkModule cmake 第4075行可以修改Debug的库后缀 VTK 9 2 0 src CMake vtkModule cmake 第4230行可以修改Debug的库后
  • 游戏开发unity打包相关系列:使用IL2CPP时打包windows程序出现Currently selected scripting backend (IL2CPP) is not installed

    安装对应平台需要的构建支持
  • NPN与PNP型传感器的区别

    NPN与PNP型传感器其实就是利用三极管的饱和和截止 输出两种状态 属于开关型传感器 但输出信号是截然相反的 即高电平和低电平 NPN输出是低电平0 PNP输出的是高电平1 沧正称重传感器 NPN与PNP型传感器 开关型 分为六类 1 NP
  • Unity3D开发小贴士(十四)JsonUtility

    Json是现在非常常用的数据格式 因为 Net的版本问题 所有没有很方便的方法可以直接在Unity里面使用C 官方的Json库 于是Unity3D自己提供了自己的一套Json工具 JsonUtility 参考下面的示例 using Unit
  • 前端学习--多益

    什么是跨域 它主要解决什么问题 如果你有8个不同的css样式 整合进网站的最好方式是 如果仅需要引入一个CSS文件 则使用连接方式 如果需要引入多个CSS文件 则首先用连接方式引入一个 目录 CSS文件 这个 目录 CSS文件中再使用导入式
  • 2023年第三届智能制造与自动化前沿国际会议(CFIMA 2023)

    2023年第三届智能制造与自动化前沿国际会议 CFIMA 2023 重要信息 会议网址 www cfima org 会议时间 2023年6月9 11日 召开地点 中国大理 截稿时间 2023年4月20日 录用通知 投稿后2周内 收录检索 E
  • Spring boot整合pagehelper

    一 导入分页插件依赖
  • ZVM Bugs (持续更新)

    问题1 Cmake配置问题 CMake Error at CMakeLists txt 5 find package Could not find a package configuration file provided by Zephy
  • can通道采样频率_CAN总线基础(上)

    概述 汽车电子设备的不断增多 对汽车上的线束分布以及信息共享与交流提出了更高的要求 传统的电气系统往往采用单一连接的方式通信 这必将带来线束的冗余以及维修的成本的提高 传统的单一通信的对接方式 已经不能满足现代汽车电子发展的需求 采用更为先
  • 软件设计师——多媒体基础

    文章目录 音频相关概念 图像相关概念 媒体的种类 多媒体相关计算 常见多媒体标准 数据压缩 有损压缩与无损压缩 题目举例 软件设计师中该部分分值为 1 3 分 音频相关概念 次声波 小于20Hz 超声波 大于20kHz A D转换 采样 g
  • dockerfile使用报错记录

    使用centos镜像默认是8 报错 解决 修改源 RUN cd etc yum repos d RUN sed i s mirrorlist mirrorlist g etc yum repos d CentOS RUN sed i s b
  • pytorch预训练模型加载与使用(以AlexNet为例)

    目录 1 概况 2 代码讲解 2 1 加载必要的包 2 2 设置GPU和transform 2 3 数据预处理 2 4 引入模型 2 5 训练模型 2 6 测试模型 2 7 保存模型 3 完整代码 4 结果 本文主要是提供过程 不要在意结果
  • 前沿技术,目前为止功能最全最强大的PLC智能远程模块,物联网模块

    前沿技术 目前为止功能最全最强大的PLC智能远程模块 物联网模块 如下图 巨控PLC智能远程控制终端不同应用场合的不同型号 巨控GRM模块分为以下4大类 GRMOPC GRM530 GRM230 GRM110 一 巨控GRMOPC系列的PL
  • 线程池有几种创建方式?

    总体来说线程池的创建可以分为以下两类 通过 ThreadPoolExecutor 手动创建线程池 通过 Executors 执行器自动创建线程池 而以上两类创建线程池的方式 又有 7 种具体实现方法 这 7 种实现方法分别是 Executo