Java-ForkJoinPool(线程池-工作窃取算法)

2023-05-16

文章目录

    • 概述
    • 工作窃取算法
    • 工作窃取算法的优缺点
    • 使用 ForkJoinPool 进行分叉和合并
    • ForkJoinPool使用
      • RecursiveAction
      • RecursiveTask
    • Fork/Join 案例Demo

概述

在这里插入图片描述

Fork 就是把一个大任务切分为若干个子任务并行地执行,Join 就是合并这些子任务的执行结果,最后得到这个大任务的结果。Fork/Join 框架使用的是工作窃取算法。

工作窃取算法

工作窃取算法是指某个线程从其他队列里窃取任务来执行。对于一个比较大的任务,可以把它分割为若干个互不依赖的子任务,为了减少线程间的竞争,把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。但是,有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务需要处理,于是它就去其他线程的队列里窃取一个任务来执行。由于此时它们访问同一个队列,为了减小竞争,通常会使用双端队列。被窃取任务的线程永远从双端队列的头部获取任务,窃取任务的线程永远从双端队列的尾部获取任务。

在这里插入图片描述

工作窃取算法的优缺点

优点:充分利用线程进行并行计算,减少了线程间的竞争。
缺点:双端队列只存在一个任务时会导致竞争,会消耗更多的系统资源,因为需要创建多个线程和多个双端队列。

使用 ForkJoinPool 进行分叉和合并

ForkJoinPool 在 Java 7 中被引入。它和 ExecutorService 很相似,除了一点不同。ForkJoinPool 让我们可以很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给 ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。可能听起来有些抽象,因此本节中我们将会解释 ForkJoinPool 是如何工作的,还有任务分割是如何进行的。

分叉和合并解释

在我们开始看 ForkJoinPool 之前我们先来简要解释一下分叉和合并的原理。
分叉和合并原理包含两个递归进行的步骤。两个步骤分别是分叉步骤和合并步骤。

分叉

一个使用了分叉和合并原理的任务可以将自己分叉(分割)为更小的子任务,这些子任务可以被并发执行。如下图所示:

在这里插入图片描述

通过把自己分割成多个子任务,每个子任务可以由不同的 CPU 并行执行,或者被同一个 CPU 上的不同线程执行。

只有当给的任务过大,把它分割成几个子任务才有意义。把任务分割成子任务有一定开销,因此对于小型任务,这个分割的消耗可能比每个子任务并发执行的消耗还要大。

什么时候把一个任务分割成子任务是有意义的,这个界限也称作一个阀值。这要看每个任务对有意义阀值的决定。很大程度上取决于它要做的工作的种类。

合并

当一个任务将自己分割成若干子任务之后,该任务将进入等待所有子任务的结束之中。一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。图示如下:

在这里插入图片描述

当然,并非所有类型的任务都会返回一个结果。如果这个任务并不返回一个结果,它只需等待所有子任务执行完毕。也就不需要结果的合并啦。

ForkJoinPool使用

ForkJoinPool 是一个特殊的线程池,它的设计是为了更好的配合 分叉-和-合并 任务分割的工作。ForkJoinPool 也在 java.util.concurrent 包中,其完整类名为 java.util.concurrent.ForkJoinPool。

创建一个 ForkJoinPool

你可以通过其构造子创建一个 ForkJoinPool。作为传递给 ForkJoinPool 构造子的一个参数,你可以定义你期望的并行级别。并行级别表示你想要传递给 ForkJoinPool 的任务所需的线程或 CPU 数量。以下是一个 ForkJoinPool 示例:

// 这个示例创建了一个并行级别为 4 的 ForkJoinPool。   如果是默认构造会自动识别当前电脑的cup核数进行并行
ForkJoinPool forkJoinPool = new ForkJoinPool(4);

提交任务到 ForkJoinPool

就像提交任务到 ExecutorService 那样,把任务提交到 ForkJoinPool。你可以提交两种类型的任务。一种是没有任何返回值的(一个 “行动”),另一种是有返回值的(一个"任务")。这两种类型分别由 RecursiveAction 和 RecursiveTask 表示。接下来介绍如何使用这两种类型的任务,以及如何对它们进行提交。

RecursiveAction

RecursiveAction 是一种没有任何返回值的任务。它只是做一些工作,比如写数据到磁盘,然后就退出了。一个 RecursiveAction 可以把自己的工作分割成更小的几块,这样它们可以由独立的线程或者 CPU 执行。
你可以通过继承来实现一个 RecursiveAction。示例如下:

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class MyRecursiveAction extends RecursiveAction {

    private long workLoad = 0;

    public MyRecursiveAction(long workLoad) {
        this.workLoad = workLoad;
    }

    @Override
    protected void compute() {

        //如果工作超出阈值,将任务分解成更小的任务
        if(this.workLoad > 10) {
            System.out.println("将工作负载 : " + this.workLoad);
            //将工作负载分成多个子任务
            List<MyRecursiveAction> subtasks =
                    new ArrayList<MyRecursiveAction>();
            subtasks.addAll(createSubtasks());
            //将子任务加入到任务队列中
            for(RecursiveAction subtask : subtasks){
                subtask.fork();
            }
        } else {
            System.out.println("自己完成工作量: " + this.workLoad);
        }
    }
    //将工作负载分成多个子任务
    private List<MyRecursiveAction> createSubtasks() {
        List<MyRecursiveAction> subtasks = new ArrayList<MyRecursiveAction>();
        //将工作负载分成两个子任务   24/2=12  12/2=6
        MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);
        MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);

        return subtasks;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);
        forkJoinPool.invoke(myRecursiveAction);

    }

}

在这里插入图片描述

RecursiveTask

RecursiveTask 是一种会返回结果的任务。它可以将自己的工作分割为若干更小任务,并将这些子任务的执行结果合并到一个集体结果。用法和RecursiveAction一样唯一不同的就是可以返回值

以下是一个 RecursiveTask 示例:

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

//配置RecursiveTask,返回值为Long
public class MyRecursiveTask  extends RecursiveTask<Long> {

    private long workLoad = 0;

    public MyRecursiveTask (long workLoad) {
        this.workLoad = workLoad;
    }

    @Override
    protected Long compute() {

        //如果工作超出阈值,将任务分解成更小的任务
        if(this.workLoad > 10) {
            System.out.println("将工作负载 : " + this.workLoad);
            //将工作负载分成多个子任务
            List<MyRecursiveTask > subtasks = new ArrayList<MyRecursiveTask >();
            subtasks.addAll(createSubtasks());
            //将子任务加入到任务队列中
            for(RecursiveTask subtask : subtasks){
                subtask.fork();
            }
            //等待子任务执行完,并得到其结果,并将结果相加
            long result = 0;
            for(MyRecursiveTask subtask : subtasks) {
                result += subtask.join();
            }
            return result;
        } else {
            System.out.println("自己完成工作量: " + this.workLoad);
            return 1L ;//返回计算结果
        }
    }
    //将工作负载分成多个子任务
    private List<MyRecursiveTask > createSubtasks() {
        List<MyRecursiveTask > subtasks = new ArrayList<MyRecursiveTask >();
        //将工作负载分成两个子任务   24/2=12  12/2=6
        MyRecursiveTask  subtask1 = new MyRecursiveTask (this.workLoad / 2);
        MyRecursiveTask  subtask2 = new MyRecursiveTask (this.workLoad / 2);
        subtasks.add(subtask1);
        subtasks.add(subtask2);
        return subtasks;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        MyRecursiveTask myRecursiveAction = new MyRecursiveTask(24);
        Long invoke = forkJoinPool.invoke(myRecursiveAction);
        System.out.println("最终结果: " + invoke);//4 从结果可以看出,任务被分成了4个子任务,每个子任务都是一个线程

    }

}

在这里插入图片描述

MyRecursiveTask 类继承自 RecursiveTask,这也就意味着它将返回一个 Long 类型的结果。MyRecursiveTask 示例也会将工作分割为子任务,并通过 fork() 方法对这些子任务计划执行。此外,本示例还通过调用每个子任务的 join() 方法收集它们返回的结果。子任务的结果随后被合并到一个更大的结果,并最终将其返回。对于不同级别的递归,这种子任务的结果合并可能会发生递归。

Fork/Join 案例Demo

需求:使用 Fork/Join 计算 1-10000的和,当一个任务的计算数量大于3000时拆分任务,数量小于3000时计算。

在这里插入图片描述

因为1~10000求和,耗时较少。下面我们将数据调大,求和1 ~ 59999999999(599亿),然后来对比一下使用 Fork/Join求和 和 普通求和之间的效率差异。

普通求和

    public static void main(String[] args) {
        //开始时间
        Long start = System.currentTimeMillis();
        long sum = 0l;
        for (long i = 1; i <= 59999999999L; i++) {
            sum+=i;
        }
        System.out.println(sum); //结果为负数,因为超出了long的最大值了   ,平均消耗时间:16秒
        //结束时间
        Long end = System.currentTimeMillis();
        System.out.println("消耗时间:"+(end-start));
    }

Fork/Join求和

package com;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

//配置RecursiveTask,返回值为Long
public class SumRecursiveTask   extends RecursiveTask<Long> {

    //大于3000要拆分(创建一个变量)
    //是否要拆分的临界值
    private static final long THRESHOLD = 3000L;

    //起始值
    private final long start;
    //结束值
    private final long end;

    //构造方法(传递起始值、结束值)
    public SumRecursiveTask(long start, long end) {
        this.start = start;
        this.end = end;
    }

    //任务编写完成
    @Override
    protected Long compute() {
        long length = end - start;
        //计算
        if(length < THRESHOLD){
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum +=i;
            }
            return sum;
        }else{
            //拆分
            long middle = (start + end) /2;
            SumRecursiveTask left = new SumRecursiveTask(start,middle);//从小到大
            left.fork();
            SumRecursiveTask right = new SumRecursiveTask(middle+1,end);//从大到小
            right.fork();
            return left.join() +right.join();
        }
    }

    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        //放入线程池
        ForkJoinPool pool = new ForkJoinPool();
        SumRecursiveTask task = new SumRecursiveTask(1, 59999999999L);
        Long result = pool.invoke(task);
        System.out.println("result="+result); //结果为负数,因为超出了long的最大值了   ,平均消耗时间:4秒
        Long end = System.currentTimeMillis();
        System.out.println("消耗时间:"+(end-start));
    }

}

总结: 可以发现使用工作窃取算法能大大的提高我们计算的速度,理论上只要你电脑足够快这个提升是没有上限的 ,前提是任务是可拆分的

在这里插入图片描述

点赞 -收藏-关注-便于以后复习和收到最新内容
有其他问题在评论区讨论-或者私信我-收到会在第一时间回复
在本博客学习的技术不得以任何方式直接或者间接的从事违反中华人民共和国法律,内容仅供学习、交流与参考
免责声明:本文部分素材来源于网络,版权归原创者所有,如存在文章/图片/音视频等使用不当的情况,请随时私信联系我、以迅速采取适当措施,避免给双方造成不必要的经济损失。
感谢,配合,希望我的努力对你有帮助^_^
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java-ForkJoinPool(线程池-工作窃取算法) 的相关文章

  • 安装、升级pip,但是python -m pip install --upgrade pip报错的解决办法

    问题描述 xff1a 按提示输入命令报错 xff1a 在命令窗中我按照要求 xff0c 进行升级 xff0c 敲入 python m pip install upgrade pip xff0c 但是提示我安装失败 解决办法 xff1a 这个
  • 解决windows和虚拟机内不能粘贴

    手工安装Vmware tools方法 这种方法是通过Ubuntu或者Centos7的安装命令直接从网上进行安装 xff0c 打开终端窗口直接执行下列命令即可 xff0c 安装之前要保证虚拟机可以连接外网 xff0c 要能连上软件更新服务器
  • Django表增加字段迁移数据库时报错: (1060, “Duplicate column name ‘‘XXX“)

    出现本问题的原因在于name后的字段在之前已经进行过迁移 xff0c 故而会提示已有重复列 这里不用进行什么删除操作 xff0c 我们将迁移文件中的相关语句注释掉即可 数据库迁移时提示的是django db utils InternalEr
  • Django数据库连接丢失问题的解决方法

    使用django的orm查询方式 xff0c 与数据库连接超时问题解决方法 在Django中使用mysql偶尔会出现数据库连接丢失的情况 xff0c 特别是在异步的时候 xff0c 由于长时间不操作数据库导致 xff0c 错误通常有如下两种
  • Linux服务器:解决报错 E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用) ’问题

    问题描述 xff1a sudo apt get install gcc python dev 解决办法 xff1a sudo rm r f var lib dpkg lock frontend sudo rm r f var lib dpk
  • 外派到某项目帮忙,但受到排斥怎办?

    问题描述 xff1a 类似情况很多 xff0c 列举几个例子 xff1a 1 xff09 A方成立了项目小组负责某项目 xff0c 项目经理是小甲 xff1b B方是利益相关方 xff0c 派小乙到A方加入该项目组 xff0c 美名其曰 x
  • MariaDB 数据库 “Error 1040 too many connection” 解决办法:MariaDB修改最大连接数

    1 mariadb数据库最大连接数 xff0c 默认是151 MariaDB none gt show variables like 39 max connections 39 43 43 43 Variable name Value 43
  • 朱老师Study210开发板刷系统实验(Win10系统)

    最近入手了朱有鹏老师的嵌入式Linux核心课程和Study210开发板 xff0c 开始跟着视频进行嵌入式linux的系统学习 由于视频录的时间很早 xff0c 在跟着视频学习的过程中发现有些细节跟视频里的的对不上 xff08 主要是因为我
  • Python-常用数据类型及其用法汇总(全面、详细、具体、明了)

    整数 xff1a 11 0b11 0o11 0x11 xff08 0b为二进制 xff0c 0o为8进制 xff0c 0x为16进制 xff09 浮点数 xff1a 1 1 3 14 3 14e5 字符串 xff1a hello world
  • mybatis-day02笔记

    1 Mybatis的Dao层实现 1 1 传统开发方式 1 1 1编写UserDao接口 span class token keyword public span span class token keyword interface spa
  • RabbitMQ 订阅模型-路由模式

    订阅模型 路由模式 xff0c 此时生产者发送消息时需要指定 RoutingKey xff0c 即路由 Key xff0c Exchange 接收到消息时转发到与 RoutingKey 相匹配的队列中 在 Direct 模型下 xff1a
  • RabbitMQ:订阅模型-匹配模式

    阅模型 匹配模式 xff0c 相比于前两种订阅模型 xff0c 是更细致的分组 xff0c 允许 在RoutingKey 中使用匹配符 xff1a 匹配一个单词 xff1a 匹配0个或多个单词 RabbitMQ 订阅模型 匹配 xff08
  • 分布式理论协议与算法 第一弹 CAP理论

    CAP 理论 xff0c 也被称为 CAP 协议 xff0c 指的是在一个分布式系统中 xff0c 最多只能同时满足 一致性 xff08 Consistency xff09 可用性 xff08 Availability xff09 和 分区
  • MongoDB:基础概述

    MongoDB 是一个开源的 跨平台的 面向文档的 基于分布式文件存储的数据库系统 xff0c MongoDB 是由 C 43 43 语言开发 xff0c 旨在为 Web 应用提供可扩展的高性能数据存储解决方案 在高负载的情况下 xff0c
  • MongoDB:安装配置

    MongoDB有两个服务器版本 xff1a MongoDB 社区版 和 MongoDB 企业版 此篇主要介绍 MacOS 下 MongoDB 社区版的安装 xff0c 在 版本 下拉列表中 xff0c 选择要下载的 MongoDB 版本 x
  • 我的三周年创作纪念日

    文章目录 一 CSDN 心路历程1 关于第一篇文章2 初拾 CSDN3 重逢 CSDN 二 CSDN 收获1 关于粉丝2 关于文章 三 文章创作工具分享1 Typora2 PicGo3 作图工具 四 憧憬 一 CSDN 心路历程 1 关于第
  • 购买公司股票 = 变相降薪?

    一些公司会扣除你部分薪金 xff0c 美名其曰购买公司股份 xff0c 为了让你放心 xff0c 也会在合同中写明退出机制 xff0c 例如 xff1a 离职是可原额退回 但当你真的要离职的时候 xff0c 这事情就变得很扯皮了 其实让你入
  • MongoDB:常用命令

    本篇内容主要介绍 xff1a MongoDB 数据库命令 MongoDB 集合命令 MongoDB 文档命令 MongoDB 文档查询拓展 文章目录 一 MongoDB 数据库命令1 启动 MongoDB2 连接 MongoDB3 获取帮助
  • 分布式理论协议与算法 第二弹 ACID原则

    ACID 原则是在 1970年 被 Jim Gray 定义 xff0c 用以表示事务操作 xff1a 一个事务是指对数据库状态进行改变的一系列操作变成一个单个序列逻辑元操作 xff0c 数据库一般在启动时会提供事务机制 xff0c 包括事务
  • MongoDB:在 Java 中使用 MongoDB

    除了通过启动 mongo 进程进如 Shell 环境访问数据库外 xff0c MongoDB 还提供了其他基于编程语言的访问数据库方法 MongoDB 官方提供了 Java 语言的驱动包 xff0c 利用这些驱动包可使用多种编程方法来连接并

随机推荐

  • 分布式理论协议与算法 第三弹 BASE理论

    大部分人解释这 CAP 定律时 xff0c 常常简单的表述为 xff1a 一致性 可用性 分区容错性三者你只能同时达到其中两个 xff0c 不可能同时达到 实际上这是一个非常具有误导性质的说法 xff0c 而且在 CAP 理论诞生 12 年
  • Docker:独具魅力的开源容器引擎

    Docker 是一个开源的应用容器引擎 xff0c 让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中 xff0c 然后发布到任何流行的 Linux 或 Windows操作系统的机器上 xff0c 也可以实现虚拟化 容器是完全使用沙箱
  • 在不同环境下 Docker 的安装部署

    本篇内容主要介绍了 xff1a Docker xff1a 不同环境下的安装部署 xff0c 包括 xff0c Docker 在 Centos7 下的安装 Docker 在 MacOS 下的安装 Docker 在 Windows 下的安装 以
  • Docker 应用实践-镜像篇

    一个 Docker 镜像往往是由多个镜像层 xff08 可读层 xff09 叠加而成 xff0c 每个层仅包含了前一层的差异部分 xff0c 单个镜像层也往往可以看作镜像使用 xff0c 当我们启动一个容器的时候 xff0c Docker
  • 如何通过限流算法防止系统过载

    限流算法 xff0c 顾名思义 xff0c 就是指对流量进行控制的算法 xff0c 因此也常被称为流控算法 我们在日常生活中 xff0c 就有很多限流的例子 xff0c 比如地铁站在早高峰的时候 xff0c 会利用围栏让乘客们有序排队 xf
  • Docker 应用实践-容器篇

    在 Docker 镜像篇中 xff0c 我们了解到 Docker 镜像类似于模板 xff0c 那么 Docker 容器就相当于从模板复制过来运行时的实例 xff0c Docker 容器可以被创建 复制 暂停和删除等 每一个 Docker 容
  • Java中Json字符串和Java对象的互转

    JSON xff08 JavaScript Object Notation xff09 是一种轻量级的数据交换格式 诞生于 2002 年 易于人阅读和编写 同时也易于机器解析和生成 JSON 是目前主流的前后端数据传输方式 JSON 采用完
  • 老板必看:1.初创业团队没有激情,咋办? 2.小股东的选择

    内容摘要 xff1a 本文有两个来自真实情况的案例 xff0c 因为涉及到 私隐 xff0c 部分内容经过处理 两个案例分别是 xff1a 1 xff09 新创业团队员工积极性差的问题 xff1b 2 xff09 小股东在两大股东的斗法中的
  • 持续事务管理过程中的事件驱动

    比较官方的定义 xff1a 事件驱动是指在持续事务管理过程中 xff0c 进行决策的一种策略 xff0c 即跟随当前时间点上出现的事件 xff0c 调动可用资源 xff0c 执行相关任务 xff0c 使不断出现的问题得以解决 xff0c 防
  • Docker 应用实践-仓库篇

    目前 Docker 官方维护了一个公共仓库 Docker Hub xff0c 用于查找和与团队共享容器镜像 xff0c 界上最大的容器镜像存储库 xff0c 拥有一系列内容源 xff0c 包括容器社区开发人员 开放源代码项目和独立软件供应商
  • 浅谈网络中接口幂等性设计问题

    所谓幂等性设计 xff0c 就是说 xff0c 一次和多次请求某一个资源应该具有同样的副作用 用数学的语言来表达就是 xff1a f x 61 f f x 在数学里 xff0c 幂等有两种主要的定义 在某二元运算下 xff0c 幂等元素是指
  • 分布式系统中的补偿机制设计问题

    我们知道 xff0c 应用系统在分布式的情况下 xff0c 在通信时会有着一个显著的问题 xff0c 即一个业务流程往往需要组合一组服务 xff0c 且单单一次通信可能会经过 DNS 服务 xff0c 网卡 交换机 路由器 负载均衡等设备
  • 关于基于标准库函数与基于HAL库函数的stm32编程方式的差异

    在之前的博客中 xff0c 我已经使用过通过标准库函数和HAL库函数对stm32进行编译工作 xff0c 在这篇博文里 xff0c 我将对之前的进行总结 关于标准库函数 由于stm32系列有着很多不同的芯片 xff0c 其所使用的寄存器也大
  • curl wget pip git-clone yum apt-get的区别

    在linux中 xff0c 会常用到这些命令进行文件下载 xff0c 软件安装以及url访问 xff0c 但总是分不清楚什么时候用什么命令去下载或者安装和访问 这里将这几个命令的用法和区别进行一个说明 xff0c 方便大家学习和记忆 1 首
  • CSS 三种样式

    本节我们要学习一下 CSS 样式的几种形式 xff0c 在实际应用中向 HTML 中引入 CSS 样式的方法有三种 xff0c 分别是行内样式 内部样式 外部样式 我们会依次学习这三种方式的优缺点以及应用场景 xff0c 本节我们先来讲一下
  • JavaFx-报错WindowsNativeRunloopThread

    问题 解决办法 需要卸载掉JDK1 8 并且将环境变量中的 34 JAVA HOME 34 指向改成JDK11的目录 点赞 收藏 关注 便于以后复习和收到最新内容 有其他问题在评论区讨论 或者私信我 收到会在第一时间回复 在本博客学习的技术
  • Gradle-JDK版本问题导致运行失败

    问题 解决办法 因为当前我们使用jdk8去运行Gradle 但是Gradle意思是必须使用11 43 的jdk版本 下面这个问题就是因为 我们默认是 改为 点赞 收藏 关注 便于以后复习和收到最新内容 有其他问题在评论区讨论 或者私信我 收
  • 权力的游戏,我是小股东,咋办?

    案例简述 xff1a 某初创业公司 xff0c 有A B两个大股东 xff0c 股份份额一样大 xff0c 另外还有一个小股东C A股东负责市场和销售 xff0c B股东负责研发和技术 xff0c B曾经是C的上司 xff0c 将C带入公司
  • Java-高版本没有jre的问题

    解决方案 jre 文件夹是可以用命令自动生成的 xff0c 在window环境中 xff0c 进入jdk目录所在的文件夹 xff0c 运行下面命令就会自动 生成jre文件夹 bin span class token punctuation
  • Java-ForkJoinPool(线程池-工作窃取算法)

    文章目录 概述工作窃取算法工作窃取算法的优缺点使用 ForkJoinPool 进行分叉和合并ForkJoinPool使用RecursiveActionRecursiveTask Fork Join 案例Demo 概述 Fork 就是把一个大