Fork/Join框架

Fork/Join框架

  • Stream的并行模式使用了Fork/Join框架,这里简单说Fork/Join框架是什么?Fork/Join框架是java7中加入的一个并行任务框架,可以将任务拆分为多个小任务,每个小任务执行完的结果在合并成为一个结果。在任务的执行过程中使用工作窃取(work-stealing)算法,减少线程之间的竞争

Stream是怎么实现的

  • 先看下整体类图:蓝色箭头代表继承,绿色箭头代表实现,红色箭头代表内部类

  • 实际上Stream只有两种操作,中间操作、终端操作,中间操作只是一种标记,只有终端操作才会实际触发执行。所以Stream流水线式的操作大致应该是用某种方式记录中间操作,只有调用终端操作才会将所有的中间操作叠加在一起在一次迭代中全部执行

操作怎么记录

  • Stream的操作记录是通过ReferencePipeline记录的,ReferencePipeline有三个内部类Head、StatelessOp、StatefulOp,Stream中使用Stage的概念来描述一个完整的操作,并用某种实例化后的ReferencePipeline来代表Stage,Head用于表示第一个Stage,即调用诸如Collection.stream()方法产生的Stage,很显然这个Stage里不包含任何操作,StatelessOp和StatefulOp分别表示无状态和有状态的Stage,对应于无状态和有状态的中间操作

操作怎么叠加

  • 操作是记录完了,但是前面的Stage并不知道后面Stage到底执行了哪种操作,以及回调函数是哪种形式。这就需要有某种协议来协调相邻Stage之间的调用关系

  • 这种协议由Sink接口完成,Sink接口包含的方法如下表所示:

  • void begin(long size),开始遍历元素之前调用该方法,通知Sink做好准备。

  • void end(),所有元素遍历完成之后调用,通知Sink没有更多的元素了。

  • boolean cancellationRequested(),是否可以结束操作,可以让短路操作尽早结束。

  • void accept(T t),遍历元素时调用,接受一个待处理元素,并对元素进行处理。Stage把自己包含的操作和回调方法封装到该方法里,前一个Stage只需要调用当前Stage.accept(T t)方法就行了。

  • 每个Stage都会将自己的操作封装到一个Sink里,前一个Stage只需调用后一个Stage的accept()方法即可,并不需要知道其内部是如何处理的。有了Sink对操作的包装,Stage之间的调用问题就解决了,执行时只需要从流水线的head开始对数据源依次调用每个Stage对应的Sink.{begin(), accept(), cancellationRequested(), end()}方法就可以了

操作怎么执行?

  • Sink完美封装了Stream每一步操作,并给出了[处理->转发]的模式来叠加操作。这一连串的齿轮已经咬合,就差最后一步拨动齿轮启动执行。是什么启动这一连串的操作呢?也许你已经想到了启动的原始动力就是结束操作(Terminal Operation),一旦调用某个结束操作,就会触发整个流水线的执行

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
您可以使用Fork/Join框架来实现批量插入操作。Fork/Join框架是Java提供的一个并行计算框架,可以将任务分解成更小的子任务,然后并行执行这些子任务,最后将结果合并返回。 首先,您需要定义一个批量插入任务,该任务将数据分割成更小的块,并在每个子任务中执行插入操作。然后,使用Fork/Join框架来执行这些子任务并等待结果。 下面是一个简单的示例代码: ```java import java.util.Arrays; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; public class BatchInsertTask extends RecursiveAction { private static final int THRESHOLD = 100; // 设置阈值,控制每个子任务的大小 private int[] data; public BatchInsertTask(int[] data) { this.data = data; } @Override protected void compute() { if (data.length <= THRESHOLD) { // 执行插入操作 insertData(); } else { // 分割数据 int mid = data.length / 2; int[] leftData = Arrays.copyOfRange(data, 0, mid); int[] rightData = Arrays.copyOfRange(data, mid, data.length); // 创建子任务 BatchInsertTask leftTask = new BatchInsertTask(leftData); BatchInsertTask rightTask = new BatchInsertTask(rightData); // 并行执行子任务 invokeAll(leftTask, rightTask); } } private void insertData() { // 执行插入操作,例如向数据库中批量插入数据 // ... System.out.println("Insert data: " + Arrays.toString(data)); } public static void main(String[] args) { // 创建ForkJoinPool ForkJoinPool forkJoinPool = new ForkJoinPool(); // 创建批量插入任务 int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; BatchInsertTask task = new BatchInsertTask(data); // 提交任务到ForkJoinPool并等待结果 forkJoinPool.invoke(task); } } ``` 在上述示例中,我们首先定义了一个`BatchInsertTask`类,继承自`RecursiveAction`类,重写了`compute()`方法来执行插入操作。在`compute()`方法中,我们通过判断数据大小是否超过阈值来决定是执行插入操作还是继续分割数据并创建子任务。在`insertData()`方法中,您可以执行实际的插入操作。 在`main()`方法中,我们创建了一个`ForkJoinPool`对象,并通过`invoke()`方法提交批量插入任务,并等待任务执行完成。 请注意,上述示例中的插入操作只是一个示例,您需要根据您的实际需求来实现具体的插入逻辑。另外,您还可以根据实际情况调整阈值大小以获取更好的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT枫斗者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值