首页 > 其他分享 >ForkJoinPool实践

ForkJoinPool实践

时间:2023-02-21 18:33:13浏览次数:49  
标签:end int ForkJoinPool 实践 util start java

最近在看一本15年出版的《Java并发编程的艺术》一书,其中看到并发编程时间部分的ForkJoinPool功能时,突然发现这个功能实际使用上就是把一个大任务分成多个小的子任务,然后使用多个线程完成。

这个场景跟我之前写过的自定义Java自定义异步功能实践有点异曲同工之妙,只不过这里有有个子任务的概念,多个任务执行结果是具有相关性的。资料指出ForkJoinPool比较适合计算密集型的任务。

性能测试中QPS取样器和RT取样器中,有这样一个使用场景,在用例执行过程中,我想了解一下当前用例执行的QPS和RT信息,就需要有个触发开关,开始收集这些数据,等某一个终止条件被触发,结束收集,然后计算结果。在用例QPS超过10万的情况下,单次收集的数据可能会超过100万,计算QPS和RT就非常适合ForkJoinPool来完成。

如果一直实时展示或者上报这些信息的话,也可以使用ForkJoinPool来完成计算功能。这里还有另外的方案来实现,如果只是得到QPS和RT数据的话,比ForkJoinPool更加合适,这里先不分享了。

ForkJoinPool API相比较ExecutorService还是比较简单的。主要的功能3个:创建任务的ForkJoinPool、创建任务分配规则和收集任务结果。

下面我以一个数组求和的Demo演示一下ForkJoinPool的功能。

首先我们需要定义一个ForkJoinPool,通常使用java.util.concurrent.ForkJoinPool#ForkJoinPool(int)或者java.util.concurrent.ForkJoinPool#commonPool这两个方法其中之一,如果你使用JDK 7及以前的版本,第二个API是不存在的。

翻看源码之后,看起来ForkJoinPool构造方法参数还是挺多的,如果都要自定义比较麻烦也是没多大必要的,所以我就选上面提到的第一种API来创建ForkJoinPool。

然后我们要创建一个任务类实现任务分配规则,首先继承java.util.concurrent.RecursiveTask实现java.util.concurrent.RecursiveTask#compute方法。

拆分任务的思路如下:使用两个int属性,标记List中需要求和片段索引。这样每次分配任务的时候,只需要改变索引值即可。将一个很长的List求和分成N个小片段求和。

类代码设计如下:

import com.funtester.frame.SourceCode
import groovy.util.logging.Log4j2

import java.util.concurrent.ForkJoinPool
import java.util.concurrent.RecursiveTask

@Log4j2
class ForkJoinT extends RecursiveTask<Integer> {

    static def data = 1..100 as List

    int start
    int end

    ForkJoinT(int start, int end) {
        this.start = start
        this.end = end
    }

    @Override
    protected Integer compute() {
        if (end - start < 5) {
            sum(start, end)
        } else {
            def middle = ((start + end) / 2) as int
            def left = new ForkJoinT(start, middle)
            def right = new ForkJoinT(middle + 1, end)
            left.fork()
            right.fork()
            left.join() + right.join()
        }
    }

    /**
     * 片段求和
     * @param i
     * @param k
     * @return
     */
    static def sum(int i, int k) {
        SourceCode.range(i, k + 1).map(data::get).sum()
    }

}

总体感觉java.util.concurrent.RecursiveTask#compute方法写起来有点像递归,思路明确了以后还是很流畅的。

先来个高斯求和,下面是测试代码:

    static void main(String[] args) {
        def pool = new ForkJoinPool(5)
        def t = new ForkJoinT(0, data.size() - 1)
        pool.submit(t)
        log.info("sum: {}", t.get())
    }

控制台输出:

22:30:42.725 main sum: 5050

性能方面等我先研究一波JMH之后再来。

标签:end,int,ForkJoinPool,实践,util,start,java
From: https://blog.51cto.com/FunTester/6076928

相关文章

  • 基于kubeasz的Kubernetes部署最佳实践
    一、环境准备1、三台8核/16G内存/500G高速硬盘服务器和三台16核/32G内存/500G高速硬盘服务器2、CentOS73、关闭selinux和关闭防火墙4、升级系统内核(......
  • B站基于ClickHouse的海量用户行为分析应用实践
    一、背景介绍 数据驱动理念已被各行各业所熟知,核心环节包括数据采集、埋点规划、数据建模、数据分析和指标体系构建。在用户行为数据领域,对常见的多维数据......
  • rtk 快速实践 part1 创建一个Post
    开发环境搭建(Typescript+RTK模板的使用)嗨,大家好,经过rtk快速上手,下面我们开始真正动手实践吖。cra创建项目提供了Typescript配合redux的模板,这个模板就是下面两行安......
  • 实践篇(三):如何有效评审软件架构图?
    作者:京东科技 倪新明设计意图的传达是架构可视化关注的重要维度,在技术方案评审过程中不可避免的会出现各种各样的架构图或设计图,这些图形化表述在设计意图传达效果层面表......
  • 火山引擎 DataTester:在广告投放场景下的 A/B 实验实践
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群“我知道在广告上的投资有一半是无用的,但问题是我不知道是哪一半。”——零售大亨约翰......
  • 测试环境docker化实践
    测试环境对于任何一个软件公司来讲,都是核心基础组件之一。测试环境伴随着发展也从单一的几套环境发展成现在的任意的docker动态环境+docker稳定环境环境体系。期间环境系统......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • 容器化构建最佳实践
    在我们的开发过程中,少不了与容器打交道,几乎所有常用的应用的都会提供构建好的容器镜像以便用户快速体验。特别是越来越多的团队使用k8s作为容器平台,在应用部署的过程中也就......