一文搞懂 java 线程池:ScheduledThreadPool 和 WorkStealingPool 原理

news2024/10/6 22:26:02

你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益:

  1. 了解大厂经验
  2. 拥有和大厂相匹配的技术等

希望看什么,评论或者私信告诉我!

文章目录

  • 一、前言
  • 二、线程池
    • 2.1 ScheduledThreadPool
      • 2.1.1 ScheduledThreadPool 简介
      • 2.1.2 ScheduledThreadPool 常用方法 scheduleAtFixedRate 和 scheduleWithFixedDelay 原理
      • 2.1.3 scheduleAtFixedRate 和 scheduleWithFixedDelay 介绍
    • 2.2 WorkStealingPool
      • 2.2.1 WorkStealingPool 简介
      • 2.2.2 ForkJoinPool 介绍
        • 2.2.2.1 ForkJoinPool 介绍
        • 2.2.2.2 ForkJoinPool 核心-工作窃取算法
        • 2.2.2.2 ForkJoinPool 核心-工作窃取算法优缺点
        • 2.2.2.3 ForkJoinPool 设计
        • 2.2.2.4 ForkJoinPool 完整例子
  • 三、总结


一、前言

上一章节我们详解介绍了SingleThreadExecutorCachedThreadPool 的原理以及应用场景,本章我们继续介绍 ScheduledThreadPoolWorkStealingPool

二、线程池

2.1 ScheduledThreadPool

2.1.1 ScheduledThreadPool 简介

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。

ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
这是它的源码

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());  //调用 ThreadPoolExecutor

}

2.1.2 ScheduledThreadPool 常用方法 scheduleAtFixedRate 和 scheduleWithFixedDelay 原理

在这里插入图片描述

DelayQueue是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在ScheduledThreadPoolExecutor中没有什么意义(设置maximumPoolSize的大小没有什么效果)

  1. 当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask。
  2. 线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务

2.1.3 scheduleAtFixedRate 和 scheduleWithFixedDelay 介绍

scheduleAtFixedRatescheduleWithFixedDelay 都是 ScheduledExecutorService 接口中用于定时执行任务的方法,它们之间的区别在于任务执行的规则:

  1. scheduleAtFixedRate 方法会按照固定的频率执行任务,不考虑任务的实际执行时间。即使前一个任务执行花费的时间超过了频率时间,后续任务也会在规定的频率内执行。例如,如果设定间隔时间为3秒,但任务执行时间为5秒,则任务将按照5秒的间隔执行。
  2. scheduleWithFixedDelay 方法会在前一个任务执行完成后的固定延迟时间后再执行下一个任务。即会等待上一个任务执行完成后才会执行下一个任务。例如,设定延迟时间为3秒,任务执行时间为5秒,则相邻两个任务之间的间隔时间为8秒(5秒执行任务 + 3秒延迟)。

通过选择合适的方法,可以根据实际需求来控制任务的执行规则。scheduleAtFixedRate 更适合需要固定频率执行任务的场景,而 scheduleWithFixedDelay 更适合需要等待前一个任务执行完成后再执行下一个任务的场景。

2.2 WorkStealingPool

2.2.1 WorkStealingPool 简介

WorkStealingPool 是 Java 中的一种线程池实现。WorkStealingPool 是 ForkJoinPool 的一个特例,具有以下特点:

  1. 工作窃取算法:WorkStealingPool 使用工作窃取算法(Work-Stealing Algorithm),每个工作者线程都有一个自己的双端队列用于存储任务,当一个线程的队列为空时,它可以从其他线程的队列中窃取任务来执行,以使工作负载均衡。
  2. 分治任务:WorkStealingPool 使用分治任务的方式来执行任务,可以高效地处理需要递归地分解任务的情况,例如在多核处理器系统中执行并行计算任务。
  3. 并行执行:WorkStealingPool 可以根据需要创建多个工作者线程来并行执行任务,适用于处理需要并行计算或处理的场景。
  4. 自动管理线程数:WorkStealingPool 可以根据需要动态地创建或关闭工作者线程,使得线程数能够根据任务情况和系统资源进行动态调整,提高性能和资源利用率。

由于 WorkStealingPool 使用工作窃取算法和分治任务的方式来执行任务,可以提高并行任务的执行效率和性能。在一些需要处理并行计算、递归分解任务或需要高效利用多核处理器的场景下,WorkStealingPool 是一个很好的选择。

它的源码实现

public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool
        (Runtime.getRuntime().availableProcessors(),
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

2.2.2 ForkJoinPool 介绍

2.2.2.1 ForkJoinPool 介绍

Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。

g-blog.csdnimg.cn/img_convert/3e5beb6031a8c6df7ff9093baedf5356.png)

2.2.2.2 ForkJoinPool 核心-工作窃取算法

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。

假如我们需要做一个比较大的任务,可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。比如A线程负责处理A队列里的任务。但是,有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。

在这里插入图片描述

2.2.2.2 ForkJoinPool 核心-工作窃取算法优缺点

工作窃取(Work-Stealing)算法是一种用于线程池中任务调度的高效机制。以下是工作窃取算法的一些优点和缺点:

优点:

  1. 负载均衡:

    • 工作窃取算法可以实现任务的动态负载均衡,当某些线程忙碌时,空闲线程可以从其他线程的队列中窃取任务执行,使得整体任务分配更加均衡。
  2. 减少竞争:

    • 不同于传统的线程池中将任务分配给线程执行,工作窃取算法中线程会主动从其他线程的队列中获取任务执行,这减少了线程之间的争夺,降低了同步和竞争的开销。
  3. 提高效率:

    • 工作窃取算法能够更好地利用多核处理器的特性,实现更高效的并发执行,尤其适用于大量计算密集型任务的并行处理。
  4. 适应动态性:

    • 在任务执行过程中,工作窃取算法可以适应动态的负载情况,动态调整任务的分配,以更好地适应不同的任务执行情况。

缺点:

  1. 内存消耗:

    • 由于工作窃取算法需要维护每个线程的工作队列,可能会增加额外的内存消耗,尤其是当线程数量较多时,需要维护多个队列。
  2. 数据局部性降低:

    • 在工作窃取算法中,线程会从其他线程的队列中窃取任务执行,这可能导致数据在不同线程之间频繁传输,降低了数据局部性,影响缓存的效率。
  3. 竞争情况:

    • 尽管工作窃取算法减少了线程之间的竞争,但在真实情况下,仍可能出现一些竞争状况,比如多个线程同时尝试窃取任务时可能会发生竞争。
  4. 复杂性:

    • 实现工作窃取算法需要考虑到线程之间的协调和通信,这增加了算法的复杂性,可能需要更多的编程和调试工作。

尽管工作窃取算法有一些局限性,但在处理大规模并行任务时,它仍然是一种高效的任务调度算法,能够提高并行计算的效率和性能。

2.2.2.3 ForkJoinPool 设计

第一步 分割任务。首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还
是很大,所以还需要不停地分割,直到分割出的子任务足够小。

第二步 执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分
别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程
从队列里拿数据,然后合并这些数据。

Fork/Join使用两个类来完成以上两件事情:

ForkJoinTask:我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务
中执行fork()和join()操作的机制。通常情况下,我们不需要直接继承ForkJoinTask类,只需要继
承它的子类,Fork/Join框架提供了以下两个子类。

  • RecursiveAction:用于没有返回结果的任务。
  • RecursiveTask:用于有返回结果的任务。

ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行。
任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当
一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任

2.2.2.4 ForkJoinPool 完整例子
class FibonacciTask extends RecursiveTask<Integer> {
    private final int n;

    public FibonacciTask(int n) {
        this.n = n;
    }

    @Override
    protected Integer compute() {
        if (n <= 1) {
            return n;
        } else {
            FibonacciTask task1 = new FibonacciTask(n - 1);
            FibonacciTask task2 = new FibonacciTask(n - 2);
            task1.fork(); // 异步执行第一个子任务
            return task2.compute() + task1.join(); // 执行第二个子任务并等待第一个子任务完成
        }
    }
}

public class FibonacciMain {
    public static void main(String[] args) {
        int n = 10; // 计算斐波那契数列的第n项

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        FibonacciTask fibonacciTask = new FibonacciTask(n);
        int result = forkJoinPool.invoke(fibonacciTask);

        System.out.println("Fibonacci number at position " + n + " is: " + result);
    }
}

三、总结

文章重点在于阐述 ScheduledThreadPool 和 WorkStealingPool 的原理及应用。ScheduledThreadPool 适用于定时任务,而 WorkStealingPool 和 ForkJoinPool 适用于并行计算和分治任务,特别是能够充分利用多核 CPU 的计算能力。文章通过代码示例和图解,清晰地解释了这两种线程池的工作机制和优势

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

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

相关文章

Google Gemini:大上下文窗口是杀手级功能吗?

在八个月前&#xff0c;一封泄露的谷歌电子邮件透露&#xff0c;谷歌正努力超越其人工智能竞争对手。谷歌不仅没有围绕其人工智能产品的护城河——换句话说&#xff0c;没有建立起商业优势——也没有可以改变现状的秘诀。就在他们努力解决这个问题的同时&#xff0c;他们也看到…

SpringSecurity中文文档(Servlet Password Storage)

存储机制&#xff08;Storage Mechanisms&#xff09; 每种支持的读取用户名和密码的机制都可以使用任何支持的存储机制&#xff1a; Simple Storage with In-Memory AuthenticationRelational Databases with JDBC AuthenticationCustom data stores with UserDetailsServic…

2024/07/04

1、梳理笔记(原创) 2、终端输入一个日期&#xff0c;判断是这一年的第几天 scanf("%d-%d-%d",&y,&m,&d); 闰年2月29天&#xff0c;平年2月28天 #include<stdio.h> int main(int argc, char const *argv[]) {int y0,m0,d0;printf("please ente…

KBL610-ASEMI无人机专用整流桥KBL610

编辑&#xff1a;ll KBL610-ASEMI无人机专用整流桥KBL610 型号&#xff1a;KBL610 品牌&#xff1a;ASEMI 封装&#xff1a;KBL-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;6A 功率(Pd)&#xff1a;中小功率 芯片个数&#xff1a…

【每天学会一个渗透测试工具】SQLmap安装教程及使用

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨SQLmap简介 Sqlmap是一款开源的渗透测试工具 &#x1f680;下载及安装 下载地址&#xff1a;http://sqlmap.org/ windo…

黑马点评DAY4|整体项目介绍、短信登录模块

项目整体介绍 项目功能介绍 项目结构 该项目前后端分离架构模式&#xff0c;后端部署在Tomcat服务器&#xff0c;前端部署在Niginx服务器上&#xff0c;这也是现在企业开发的标准做法。PC端首先向Niginx发起请求&#xff0c;得到页面的静态资源&#xff0c;页面再通过ajax向服…

【UE5.3】笔记7 控制Pawn移动

使用A、D键控制角色左右移动 打开我们的BP_Player蓝图类&#xff0c;选择事件图表&#xff0c;添加我们的控制事件 右键&#xff0c;搜索A keyboard&#xff0c;选择A,如下图&#xff0c;D也是 添加扭矩力 首先我们要把我们的player上的模拟物理选项打开&#xff0c;这样我们…

Windows系统安装MySQL8.0.38

MySQL 8.0 相对于 MySQL 5.7 来说有几个显著的区别和改进&#xff0c;以下是一些主要的区别&#xff1a; JSON 数据类型和函数改进&#xff1a; MySQL 8.0 引入了更多的 JSON 支持&#xff0c;包括 JSON 数据类型、JSON 函数和操作符。这使得存储和查询 JSON 数据更加方便和高效…

sql语句练习注意点

1、时间可以进行排序&#xff0c;也可以用聚合函数对时间求最大值max&#xff08;时间&#xff09; 例如下面的例子&#xff1a;取最晚入职的人&#xff0c;那就是将入职时间倒序排序&#xff0c;然后limit 1 表&#xff1a; 场景&#xff1a;查找最晚入职员工的所有信息 se…

MySQL-作业1

一、数据库 1、登陆数据库 2、创建数据库zoo 3、修改数据库zoo字符集为gbk 4、选择当前数据库为zoo 5、查看创建数据库zoo信息 6、删除数据库zoo 二、创建表 1、创建一个名称为db_system的数据库 2、在该数据库下创建两张表&#xff0c;具体要求如下&#xff1a; 员工表 user …

贵的智能猫砂盆值得买吗?包含百元、千元的高性价比品牌推荐!

对于养猫的上班族来说&#xff0c;智能猫砂盆真的是越早买越好&#xff0c;普通猫砂盆用这么久下来能把我们这些上班的都累死&#xff0c;每次一回到家就能闻到猫屎的臭味&#xff0c;一看就收获猫砂盆里满满当当的猫屎&#xff0c;在外面要上班&#xff0c;在家里也要给猫上班…

C语言数据类型和变量(三)

目录 1.赋值操作符 1&#xff09;连续赋值 2&#xff09;复合赋值符 2.单目操作符&#xff1a;、- -、&#xff08;正号&#xff09;、- &#xff08;负号&#xff09; 1&#xff09;和-- 2&#xff09; 和 - 3.强制类型转换 4.printf&#xff08;&#xff09;函数 1…

用for语句实现九九乘法表

① #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int main() {for (int i 1; i < 9; i){for (int j 1; j < i; j){printf("%d*%d%d\t", j, i, i * j);}printf("\n");}return 0; } ② #define _CRT_SECURE_NO_WARNINGS #include &…

美特CRM anotherValue FastJson反序列化RCE漏洞复现

0x01 产品简介 MetaCRM是一款智能平台化CRM软件,通过提升企业管理和协同办公,全面提高企业管理水平和运营效率,帮助企业实现卓越管理。美特软件开创性地在CRM领域中引入用户级产品平台MetaCRM V5/V6,多年来一直在持续地为客户创造价值,大幅提升了用户需求满足度与使用的满意…

前端学习(三)CSS介绍及选择符

##最近在忙期末考试&#xff0c;因此前端笔记的梳理并未及时更新。在学习语言过程中&#xff0c;笔记的梳理对于知识的加深very vital.因此坚持在明天学习新知识前将笔记梳理完整。 主要内容&#xff1a;CSS介绍及选择符 最后更新时间&#xff1a;2024/7/4 目录 内容&#x…

震惊!运气竟能如此放大!运气的惊人作用,你了解吗?

芒格&#xff1a;得到你想要的东西&#xff0c;最保险的办法&#xff0c;就是让自己配得上你想要的那个东西。今天仔细想了想这句话&#xff0c;他其实说的是无数成功人士的心声 —— “我配得上&#xff01;” 美剧《绝命毒师》有个导演叫文斯吉里根&#xff08;Vince Gilliga…

JAVA:文件防重设计指南

1、简述 在现代应用程序中&#xff0c;处理文件上传是一个常见的需求。为了保证文件存储的高效性和一致性&#xff0c;避免重复存储相同的文件是一个重要的优化点。本文将介绍一种基于哈希值的文件防重设计&#xff0c;并详细列出实现步骤。 2、设计原理 文件防重的基本思路…

「Java开发指南」如何用MyEclipse完成Spring Web Flow 2.0搭建?

本教程将引导您完成Spring Web Flow的软件组件生成&#xff0c;这是Spring的一个项目&#xff0c;用于简化Web应用程序的开发。虽然Spring Web Flow与Spring MVC兼容&#xff0c;但Spring Web Flow使用流而不是控制器来实现应用程序的Web层。在本教程中&#xff0c;您将学习如何…

跑腿平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;基础数据管理&#xff0c;管理员管理&#xff0c;接单详情管理&#xff0c;跑腿员管理&#xff0c;跑腿任务管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;跑腿任务&#xff0c;接单员&…

前端播放RTSP视频流,使用FLV请求RTSP视频流播放(Vue项目,在Vue中使用插件flv.js请求RTSP视频流播放)

简述&#xff1a;在浏览器中请求 RTSP 视频流并进行播放时&#xff0c;直接使用原生的浏览器 API 是行不通的&#xff0c;因为它们不支持 RTSP 协议。为了解决这个问题&#xff0c;开发者通常会选择使用像 flv.js 这样的库&#xff0c;它专为在浏览器中播放 FLV 和其他流媒体格…