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

你好,我是 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;}@Overrideprotected 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.rhkb.cn/news/368110.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP宜邦家政服务管理系统-计算机毕业设计源码04426

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2开发现状 1.3论文结构与章节安排 2 宜邦家政服务管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用…

力扣hot100 -- 动态规划(上)

目录 ❄技巧 &#x1f33c;爬楼梯 &#x1f354;杨辉三角 &#x1f30a;打家劫舍 &#x1f40e;完全平方数 &#x1f33c;零钱兑换 &#x1f33c;单词拆分 ❄技巧 动态规划dp-CSDN博客 &#x1f446;花 5 分钟快速刷一遍 花 10 分钟浏览一下 线性DP 背包DP&#x1f447…

VS展示6个错误中的0个解决方法

左键点击展示6个错误中的0个 左键点击展示23个警告中的0个

国产强大免费WAF, 社区版雷池动态防护介绍

雷池WAF&#xff0c;基于智能语义分析的下一代 Web 应用防火墙 使用情况 我司于2023年4月23日对雷池进行测试&#xff0c;测试一个月后&#xff0c;于2023年5月24日对雷池进行正式切换&#xff0c;此时版本为1.5.1。 里程碑纪念 后续一直跟随雷池进行版本升级&#xff0c;当前…

怎样使用js技术实现Chrome投屏功能?

在Web前端技术中&#xff0c;直接控制浏览器窗口或标签页从主屏投屏到副屏&#xff08;如PPT的演讲者模式&#xff09;并不简单&#xff0c;而且直接控制浏览器窗口从主屏投屏到副屏的功能超出了Web标准的范畴&#xff0c;并且涉及到用户系统级别的设置和权限&#xff0c;因此不…

ETCD概述--使用/特性/架构/原理

ETCD概述 ETCD是一个高度一致的分布式键值存储, 它提供了一种可靠的方式来存储需要由分布式系统或机器集群访问的数据(高可用, 强一致性)​全局的配置服务中心. 本文将介绍其特性、相关操作和常见的应用场景. 如果想了解更多, 请查阅我的技术博客: https://dingyuqi.com 特性 …

C语言分支和循环(下)

C语言分支和循环&#xff08;下&#xff09; 1. 随机数生成1.1 rand1.2 srand1.3 time1.4 设置随机数的范围 2. 猜数字游戏实现 掌握了前面学习的这些知识&#xff0c;我们就可以写⼀些稍微有趣的代码了&#xff0c;比如&#xff1a; 写⼀个猜数字游戏 游戏要求&#xff1a; 电…

git常用命令速查表

Git相关概念简述 版本库&#xff1a;git在本地开辟的一个存储空间&#xff0c;一般在 .git 文件里。工作区(workspace)&#xff1a; 就是编辑器里面的代码&#xff0c;我们平常开发直接操作的就是工作区。暂存区&#xff08;index/stage&#xff09;&#xff1a;暂时存放文件的…

13. Revit API: Filter(过滤器)

13. Revit API: Filter&#xff08;过滤器&#xff09; 前言 在讲Selection之前&#xff0c;还是有必要先了解一下的过滤器的。 对了&#xff0c;关于查找一些比较偏的功能或者API的用法&#xff0c;可以这样查找 关键词 site:https://thebuildingcoder.typepad.com/ site是…

C语言 -- 函数

C语言 -- 函数 1. 函数的概念2. 库函数2.1 标准库和头文件2.2 库函数的使用方法2.2.1 功能2.2.2 头文件包含2.2.3 实践2.2.4 库函数文档的一般格式 3. 自定义函数3.1 函数的语法形式3.2 函数的举例 4. 形参和实参4.1 实参4.2 形参4.3 实参和形参的关系 5. return 语句6. 数组做…

react ts 封装3D柱状图,支持渐变

留档&#xff0c;以防忘记 bar3D.tsx import React, { useEffect, useRef, useState } from react; import * as echarts from echarts; import echarts/lib/chart/bar; import echarts/lib/chart/pictorialBar; import echarts/lib/component/grid; import echarts/lib/comp…

Centos7 安装老版本的chrome

查看自己linux是哪个centos版本 使用以下命令&#xff1a; cat /etc/centos-release我这里是centOS 7。然后在安装最新版的google-chrome时&#xff0c;总是会报错显示存在依赖环境的问题&#xff0c;使得无法安装成功chrome。 Package: google-chrome-stable (/google-chro…

使用 Rustup 管理 Rust 版本

文章目录 安装 Rustup配置镜像源安装 Rustup 安装 RustVS Code插件创建项目代码示例 Rust 官网&#xff1a;https://www.rust-lang.org/zh-CN/Crates 包管理&#xff1a;https://crates.io/Rust 程序设计语言&#xff1a;https://kaisery.github.io/trpl-zh-cn/通过例子学 Rust…

如何对低代码平台进行分类?

现在市面上的低代码平台就像雨后春笋一样冒出来&#xff0c;而且源源不绝&#xff0c;但总结下来&#xff0c;大致的也就以下三类。 一、 aPaaS多引擎类&#xff08;有很多成熟引擎、做好东西要一起用&#xff09; 这类产品包括&#xff1a;织信Informat&#xff08;国内&…

使用 Smart-doc 记录 Spring REST API

如果您正在使用 Spring Boot 开发 RESTful API&#xff0c;您希望让其他开发人员尽可能容易地理解和使用您的 API。文档是必不可少的&#xff0c;因为它为将来的更新提供了参考&#xff0c;并帮助其他开发人员与您的 API 集成。很长一段时间以来&#xff0c;记录 REST API 的方…

uni-app上传失败超出文件限制解决方法-分包处理-预加载

分包背景 当你的上传出现一下错误&#xff1a; Error: 系统错误&#xff0c;错误码&#xff1a;80051,source size 2089KB exceed max limit 2MB [20240703 10:53:06][wxbf93dfb6cb3eb8af] [1.06.2405010][win32-x64] 说明你主包太大需要处理了&#xff0c;一下两种方法可以…

REGX52.H报错

keil cannot open source input file "REGX52.H": No such file or directory 选择下面这个目录 Keil\C51\INC\Atmel

SpringCloudAlibaba基础四 微服务调用组件OpenFeign

JAVA 项目中如何实现接口调用&#xff1f; 1&#xff09;Httpclient HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 …

C语言中的文件操作

1. 为什么使⽤⽂件 如果没有文件&#xff0c;我们写的程序的数据是存储在电脑内存中的&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;要将数据进行持久化的保存&#xff0c;我们可以使用文件。 2. 什么是⽂件 磁盘&#xff08;硬盘&#…

springboot双学位招生管理系统-计算机毕业设计源码93054

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…