Java 线程池(Thread Pools)详解

news2025/1/18 0:44:35

目录

1、线程池介绍

2、线程池执行原理

3、线程池中的阻塞队列

4、Java 线程池中的拒绝策略

5、Java 提供的创建线程池的方式

6、线程池的使用示例

7、ForkJoinPool 和 ThreadPool 的区别


1、线程池介绍

        线程池是一种重用线程的机制,用于提高线程的利用率和管理线程的生命周期,常用于多线程编程和异步编程。Java提供了多种线程池实现,其中最常用的是ThreadPoolExecutor类和Executors类提供的静态工厂方法。

        线程池由一个线程队列和一个任务队列组成,线程队列中保存着空闲线程,任务队列中保存着等待执行的任务。线程池启动后,线程池中的线程从任务队列中获取任务并执行,执行完毕后返回线程队列中等待下一次任务的到来。如果任务队列为空,线程池中的线程将等待新的任务到来或被关闭。

(1)Java中的线程池可以使用以下参数进行配置:

  1. 核心线程数(corePoolSize):线程池中的常驻线程数,即保持存活的线程数量。当任务数量小于核心线程数时,线程池中的线程数量不会减少,除非线程池被关闭。
  2. 最大线程数(maximumPoolSize):线程池中允许的最大线程数,即线程池中允许存在的最多线程数量。
  3. 任务队列(workQueue):用于保存等待执行的任务的队列,有多种实现方式,如ArrayBlockingQueue、LinkedBlockingQueue等。
  4. 线程存活时间(keepAliveTime):当线程池中的线程数量大于核心线程数时,多余的空闲线程的存活时间。如果空闲时间超过该值,多余的线程将被销毁,直到线程池中的线程数量等于核心线程数。
  5. 线程工厂(threadFactory):用于创建新线程的工厂,可以定制线程名、线程优先级等。
  6. 拒绝策略(rejectedExecutionHandler):当任务队列满时,对新任务的处理策略。有多种实现方式,如AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy等。

(2)线程池的主要优点包括:

  1. 降低线程创建和销毁的开销,提高系统性能。
  2. 提高线程的利用率和系统的吞吐量。
  3. 统一线程的管理和监控,避免线程泄漏和线程安全问题。
  4. 支持任务队列和拒绝策略等机制,提供灵活的任务调度和任务处理能力。

2、线程池执行原理

        线程池执行原理图示:

3、线程池中的阻塞队列

        Java 线程池使用阻塞队列实现线程之间的同步,控制任务的提交和执行。线程池中的任务被提交到阻塞队列中,等待被线程池中的线程执行。当线程池中的线程空闲时,它们会从阻塞队列中取出任务进行执行。

        常用的阻塞队列有以下几种:

  1. ArrayBlockingQueue:基于数组实现的有界阻塞队列,插入操作和删除操作都可能会被阻塞。
  2. LinkedBlockingQueue:基于链表实现的阻塞队列,可以指定容量,如果未指定容量,则容量默认为 Integer.MAX_VALUE。插入操作和删除操作都可能会被阻塞。
  3. SynchronousQueue:一个没有容量的阻塞队列,插入操作和删除操作必须同时进行,否则会被阻塞。// 阻塞队列,不存储元素

        阻塞队列的实现可以保证线程安全,多个线程可以同时操作队列。当阻塞队列为空时,从队列中取出任务的操作会被阻塞,直到队列中有新的任务被添加进来。当阻塞队列已满时,添加任务的操作会被阻塞,直到队列中有任务被取出。

        在线程池中使用阻塞队列可以帮助控制任务的提交速度,防止任务提交过多导致系统资源的浪费。在使用阻塞队列时需要根据具体情况选择合适的实现类,以实现更高效的任务调度和执行。

        除定时执行的线程池外,其他三种线程池创建使用的阻塞队列如下图所示:

        ArrayBlockingQueue和LinkedBlockingQueue的区别:

        ArrayBlockingQueue 底层采用数组来实现队列,因此它在创建时需要指定容量大小,并且容量不可变。由于是基于数组实现,因此 ArrayBlockingQueue 可以高效地随机访问队列中的元素,但是插入和删除操作需要移动元素,因此效率相对较低。// 适合随机访问

        LinkedBlockingQueue 底层采用链表来实现队列,因此它在创建时可以不指定容量大小,也可以指定容量大小,但是如果没有指定容量大小,则默认容量为 Integer.MAX_VALUE。由于是基于链表实现,因此 LinkedBlockingQueue 插入和删除元素时只需要修改指针,因此效率相对较高,但是不能高效地随机访问队列中的元素。// 适合频繁修改

4、Java 线程池中的拒绝策略

        Java 线程池中的拒绝策略是指当线程池中的工作队列已满,且线程池中的线程已达到最大值时,线程池无法再处理新的任务时所采取的策略。Java 线程池中提供了以下四种拒绝策略:

  1. AbortPolicy:直接抛出 RejectedExecutionException 异常,该异常可以在代码中捕获并进行处理。
  2. CallerRunsPolicy:在任务被拒绝添加后,会使用调用线程池的线程来执行该任务。
  3. DiscardOldestPolicy:将等待时间最长的任务丢弃,然后尝试将当前任务添加到工作队列中。
  4. DiscardPolicy:直接将任务丢弃,不作任何处理。

        这些拒绝策略可以通过调用 ThreadPoolExecutor 类的 setRejectedExecutionHandler() 方法进行设置。例如,以下代码将线程池的拒绝策略设置为 AbortPolicy:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    1, // 核心线程数
    10, // 最大线程数
    60, // 空闲线程存活时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<Runnable>(100), // 工作队列
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

        CallerRunsPolicy 拒绝策略:

        CallerRunsPolicy 拒绝策略,当线程池的工作队列已满且线程池中的线程数已达到最大值时,线程池无法再处理新的任务时,它会将该任务交给线程池的调用线程来执行,即使用提交任务的线程来执行该任务。

        CallerRunsPolicy 拒绝策略主要用于防止任务丢失和保证任务的顺序性。当线程池无法处理新的任务时,CallerRunsPolicy 会将任务交给调用线程来执行,这样可以确保任务不会丢失,并且可以保证任务的顺序性,即任务的执行顺序和提交顺序一致。

        但是,使用 CallerRunsPolicy 拒绝策略可能会导致调用线程被阻塞,因为调用线程需要等待任务执行完毕才能继续执行其他任务,这可能会影响整个系统的性能。因此,在选择拒绝策略时需要权衡任务执行的顺序性和系统的性能。

// 总的来说,Java自带的四种拒绝策略都很鸡肋,一般不用于生产,如何处理拒绝数据还需要根据各自的应用场景进行定制

        如何自定义线程池的拒绝策略:

        可以通过实现 RejectedExecutionHandler 接口来自定义线程池中的拒绝策略。该接口只有一个方法:

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

        当线程池无法处理新的任务时,会调用 rejectedExecution() 方法来处理拒绝的任务。在实现该方法时,可以根据实际情况进行相应的处理,比如将任务添加到其他队列中,或者记录日志等

        以下是一个自定义拒绝策略的示例代码:

public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 将拒绝的任务记录到日志中
        System.out.println("Task rejected, " + r.toString());
    }
}

        在上述代码中,CustomRejectedExecutionHandler 类实现了 RejectedExecutionHandler 接口,并重写了 rejectedExecution() 方法,当线程池无法处理新的任务时,该方法将被调用,并将拒绝的任务记录到日志中。

5、Java 提供的创建线程池的方式

        Java提供了多种创建线程池的方式,常见的方式包括:

  1. Executors类:Executors类是Java提供的线程池工具类,可以使用其提供的静态工厂方法来创建线程池,如newFixedThreadPool()、newCachedThreadPool()等。// IO密集行型
  2. ThreadPoolExecutor类:ThreadPoolExecutor是Java提供的线程池实现类,可以通过创建ThreadPoolExecutor对象来创建自定义的线程池。
  3. ForkJoinPool类:ForkJoinPool是Java提供的专门用于支持分而治之算法的线程池,可以通过创建ForkJoinPool对象来使用。// CPU密集型
  4. ScheduledExecutorService接口:ScheduledExecutorService是Java提供的用于定时执行任务的线程池接口,可以使用其实现类ScheduledThreadPoolExecutor来创建定时任务线程池。

        在选择创建线程池的方式时,应该根据具体的应用场景和需求来选择最合适的方式和参数配置。一般来说,如果需要执行大量的短期异步任务,可以使用newCachedThreadPool()创建可缓存线程池;如果需要执行一些固定的长期任务,可以使用newFixedThreadPool()创建固定大小的线程池;如果需要执行分而治之算法的任务,可以使用ForkJoinPool;如果需要执行定时任务,可以使用ScheduledExecutorService接口。同时,在使用线程池时,也应该注意线程安全、线程池参数配置等问题,以保证线程池的稳定和性能。

6、线程池的使用示例

        以下是一个使用 Java 线程池实现售票的示例:// 循环调用多次execute()方法

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

public class TicketSeller {
    private static final int NUM_TICKETS = 1000; // 总票数
    private static int numSold = 0; // 已售票数
    private static ExecutorService executor = Executors.newFixedThreadPool(5); // 线程池

    public static void main(String[] args) {
        // 创建5个售票员线程
        for (int i = 0; i < 5; i++) {
            executor.execute(new TicketSellerTask());
        }
        executor.shutdown();
    }

    static class TicketSellerTask implements Runnable {
        @Override
        public void run() {
            while (numSold < NUM_TICKETS) {
                synchronized (TicketSeller.class) { // 同步块保证线程安全
                    if (numSold < NUM_TICKETS) {
                        System.out.println(Thread.currentThread().getName() + " sold ticket " + (++numSold));
                    }
                }
            }
        }
    }
}

        在这个示例中,我们使用了一个静态的 numSold 变量来记录已售票数。我们创建了一个固定大小为 5 的线程池,并创建了 5 个 TicketSellerTask 售票员任务。在 TicketSellerTask 中,我们使用一个同步块来保证线程安全,即同时只有一个线程能够访问临界区,从而避免了多个线程同时卖同一张票的问题。

        我们通过调用 executor.execute() 方法来提交任务,该方法会从线程池中选一个空闲线程来执行任务。最后,我们关闭了线程池。

7、ForkJoinPool 和 ThreadPool 的区别

        ForkJoinPool 和 ThreadPool 都是 Java 中常用的线程池实现,但它们有一些不同之处,下面列出一些区别:

  1. 工作原理不同:ThreadPool 是一个固定大小的线程池,用于执行可运行任务和可调度任务,而 ForkJoinPool 则是基于工作窃取算法的线程池,用于处理分治任务
  2. 任务调度方式不同:ThreadPool 中的任务是由一个任务队列来维护的,线程从队列中取出任务执行;而 ForkJoinPool 中的任务是由任务队列和工作窃取算法一起调度的,任务队列用于存储待执行任务,而工作线程会从其他线程的任务队列中窃取任务执行。
  3. 执行方式不同:ThreadPool 在执行任务时是按顺序依次执行的,而 ForkJoinPool 中的任务是以分治的方式执行的,一个大的任务会被分割成多个小任务,小任务会被分配给多个线程并行执行,然后将结果合并。

        所以,ForkJoinPool 更适用于处理分治任务,可以将大任务拆分成小任务并行执行,从而提高执行效率;而 ThreadPool 更适用于处理较小的任务,以及需要按顺序执行任务的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/528330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android开发:我们很迷茫,出路在哪里?

“都说今年是互联网行业寒风刺骨&#xff0c;尤其移动端开发市场更是饱和&#xff0c;在跌跌撞撞近一个月后&#xff0c;我终于在一家小公司找到了工作。入职后&#xff0c;领导让我接手一个二手Android项目&#xff0c;项目很庞大&#xff0c;前任开发人员已离职一个多月了&am…

实现 Kubernetes 安全态势管理

Kubernetes 已经成为容器编排的事实标准。它引入了强大的管理功能&#xff0c;但也带来了一些严峻的安全挑战——尤其是在多云环境中。其中包括缺乏对设置的可见性、镜像的滥用、通信故障和监控困难。 理解 K8s 的安全挑战 Kubernetes 挑战的核心是需要以高度协调的方式管理大…

日撸 Java 三百行day51

文章目录 说明Day51 KNN 分类器1.KNN2.代码1.aff内容解读2.代码理解 说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了github上维护&#xff1a;https://github.com/fulisha-ok/sampledat…

静电防护:消除静电的秘诀!

随着现代科技的进步&#xff0c;人们对静电防护越来越重视。有的人认为消除静电是不可能做到的事情&#xff0c;但实际上并不是这样的&#xff01; 1&#xff1a;静电的产生 静电是一个非常普遍的现象&#xff0c;通常发生在5 kV电压下。静电可以产生于物体表面或环境中。如果…

电视盒子哪个牌子好?博主力荐2023目前性能最好的电视盒子

电视盒子能让电视机在不换新的前提下丰富资源、升级配置&#xff0c;是电视机的最佳拍档&#xff0c;但面对这么多的品牌让大家在选购时都会疑惑电视盒子哪个牌子好&#xff0c;博主老周盘点了目前性能最好的电视盒子&#xff0c;具体是哪些品牌呢&#xff1f;请看下文&#xf…

CMU-CERT内部威胁数据集 Insider Threat

CMU-CERT内部威胁数据集 Insider Threat CMU-CERT简介CMU-CERT版本CMU-CERT r1版本内容logon.csv内容decive.csv内容HTTP.csv内容LDAP and Administrative records勘误一些已知的缺陷 CMU-CERT网站 CMU-CERT简介 首先解释一下CMU-CERT是什么意思。 “CMU”是卡内基梅隆大学&a…

专业的Web自动化测试工具拥有哪些特点?

Web自动化测试是为了解决Web应用程序测试工程师在测试过程中的挑战和复杂性而实施的&#xff0c;可以通过自动化测试工具来实现。自动化测试工具是一种软件&#xff0c;其目的在于自动执行测试&#xff0c;提高测试效率和测试准确性&#xff0c;那专业的Web自动化测试工具拥有哪…

Learning C++ No.20【二叉树OJ题实战】

文章目录 引言&#xff1a;第一题&#xff1a;根据二叉树创建字符串第二题&#xff1a;二叉树的层序遍历第三题&#xff1a;自底向上实现层序遍历第四题&#xff1a;二叉树的最近公共祖先第五题&#xff1a;将搜索二叉树转换成双向链表第六题&#xff1a;从前序和中序遍历序列构…

什么是字符串数组

一、概念理解 1.C语言中没有字符串类型&#xff0c;用一片内存空间保存一串字符&#xff0c;这片空间称作字符数组。 2.以‘\0’结尾的字符数组被称为字符串数组。‘\0’是字符串结束的标志。 3.用双引号括起来的都是字符串。 二、初始化 char str[6] {h,e,l,l,o,\0};//字符串…

Linux网络——shell编程之sed编辑器

Linux网络——shell编程之sed编辑器 一、sed编辑器1.概述2.工作流程3.工作场景4.常用选项 二、sed编辑器基本用法1.打印操作2.打印行号3.增加操作4.插入操作5.替换操作6.删除操作7.字符转换 一、sed编辑器 1.概述 sed是一种在线编辑器&#xff0c;它一次处理一行内容。处理时&…

JWT学习

JSON Web Token&#xff08;JWT&#xff09;是目前最流行的跨域身份验证解决方案。虫虫今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1.用户向服务器发送用户名和密码。 2.验证服务器后&#xff0c;相关数据&#…

Unity 2022 Build-in、URP、HDRP对比

渲染管线对比 Platform Support平台支持Lights光照Lights灯光Shadows阴影Global Illumination全局光照Light Probes光照探针Adaptive Probe Volumes自适应探针体积Reflection Probes 反射探针 Raytracing 光线跟踪Path tracing 路径追踪Environment lighting 环境光 Color颜色H…

编译 MXNet 模型

本篇文章译自英文文档 Compile MXNet Models。 作者是 Joshua Z. Zhang&#xff0c;Kazutaka Morita。 更多 TVM 中文文档可访问 →TVM 中文站。 本文将介绍如何用 Relay 部署 MXNet 模型。 首先安装 mxnet 模块&#xff0c;可通过 pip 快速安装&#xff1a; pip install …

4、picodet 小目标训练全流程

文章目录 1、数据准备1.1 VOC转COCO2、使用sahi切图2.1 切图分析及过程可视化2.2 使用完整的切图命令进行切图2.3 对各个数据集的状态进行查看2.4 过滤数据集中不合适的框 3、转换成VOC4、生成训练数据5、模型训练6、模型推理 使用picodet进行小目标检测。 本文以检测小目标乒乓…

索洛模型(二)

索洛模型(二) 文章目录 索洛模型(二)[toc]1 事实2 假设2.1 对生产函数的假设2.2对投入要素的假设 3 索洛模型的动态学3.1 k k k的动态学3.2 平衡增长路径 4 储蓄率变化的影响4.1 对产出的影响4.2 对消费的影响 索罗经济增长模型&#xff08;Solow growth model&#xff09;&am…

ClickHouse 安装部署

文章目录 ClickHouse 安装部署一、准备环节1、确认防火墙是在关闭状态2、CentOS 取消打开文件数限制3、安装依赖4、CentOS 取消 SELINUX 二、单机搭建三、启动server ClickHouse 安装部署 一、准备环节 1、确认防火墙是在关闭状态 输入命令&#xff1a; systemctl status fi…

Centos7.6系统里安装Superset,连接ClickHouse

​ 本文是在centos 7 虚拟机中安装Superset和clickhouse&#xff0c;首先要有 安装python3环境 Centos7.6默认有python2&#xff0c;要先安装python3&#xff0c;下边这个python3安装教程很详细。 参考连接&#xff1a;CentOS7下安装Python3&#xff0c;超详细完整教程_centos…

使用vercel免费搭建vue项目

之前是通过Github作为服务器来发布静态网站&#xff0c;今天有人告诉我&#xff0c;这里有一个叫vercel的商家可以直接白嫖&#xff0c;来试试给他上一课。 1 注册账号 进入官网vercel.com进行注册&#xff0c;并且绑定自己的 Github 2 项目代码 若是自己的项目就不用管; 不是…

夏令营教育小程序开发功能和优势有哪些?

随着人们生活水平的提高&#xff0c;对于孩子的教育问题也是越来越重视&#xff0c;无论是教育方式还是教育内容上都追求新颖、多样化。在暑假期间&#xff0c;很多家长也希望孩子能够在这个长假期之间参加一些活动&#xff0c;培养孩子兴趣的同时也丰富假期内容&#xff0c;让…

【云原生进阶之PaaS中间件】第一章Redis-2.1架构综述

1 Redis组件模型 Redis 组件的系统架构如图所示&#xff0c;主要包括事件处理、数据存储及管理、用于系统扩展的主从复制/集群管理&#xff0c;以及为插件化功能扩展的 Module System 模块。 Redis的客户端与服务端的交互过程如下所示&#xff1a; 1.1 事件处理机制 Redis 中的…