ForkJoinPool介绍

一 点睛

Java7 提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。

ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。

使用方法:创建了ForkJoinPool实例之后,就可以调用ForkJoinPool的submit(ForkJoinTask task) 或invoke(ForkJoinTask task)方法来执行指定任务了。

其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象子类:RecusiveAction和RecusiveTask。其中RecusiveTask代表有返回值的任务,而RecusiveAction代表没有返回值的任务。

下面的UML类图显示了ForkJoinPool、ForkJoinTask之间的关系:

ForkJoinPool介绍_第1张图片

二 无返回值实战——通过多线程分多个小任务进行打印数据

1 代码

import java.util.concurrent.*;
// 继承RecursiveAction来实现"可分解"的任务
class PrintTask extends RecursiveAction
{
     // 每个“小任务”只最多只打印50个数
     private static final int THRESHOLD = 50;
     private int start;
     private int end;
     // 打印从start到end的任务
     public PrintTask(int start, int end)
     {
           this.start = start;
           this.end = end;
     }
     @Override
     protected void compute()
     {
           // 当end与start之间的差小于THRESHOLD时,开始打印
           if(end - start < THRESHOLD)
           {
                for (int i = start ; i < end ; i++ )
                {
                     System.out.println(Thread.currentThread().getName()
                           + "的i值:" + i);
                }
           }
           else
           {
                // 如果当end与start之间的差大于THRESHOLD时,即要打印的数超过50个
                // 将大任务分解成两个小任务。
                int middle = (start + end) / 2;
                PrintTask left = new PrintTask(start, middle);
                PrintTask right = new PrintTask(middle, end);
                // 并行执行两个“小任务”
                left.fork();
                right.fork();
           }
     }
}
public class ForkJoinPoolTest
{
     public static void main(String[] args)
           throws Exception
     {
           ForkJoinPool pool = new ForkJoinPool();
           // 提交可分解的PrintTask任务
           pool.submit(new PrintTask(0 , 300));
           pool.awaitTermination(2, TimeUnit.SECONDS);
           // 关闭线程池
           pool.shutdown();
     }
}

2 运行

ForkJoinPool-1-worker-0的i值:183
ForkJoinPool-1-worker-0的i值:184
ForkJoinPool-1-worker-0的i值:185
ForkJoinPool-1-worker-0的i值:186
ForkJoinPool-1-worker-3的i值:192
ForkJoinPool-1-worker-3的i值:193
ForkJoinPool-1-worker-3的i值:194
ForkJoinPool-1-worker-3的i值:195
......
ForkJoinPool-1-worker-3的i值:220
ForkJoinPool-1-worker-3的i值:221
ForkJoinPool-1-worker-3的i值:222
ForkJoinPool-1-worker-3的i值:223
ForkJoinPool-1-worker-3的i值:224
ForkJoinPool-1-worker-2的i值:90
ForkJoinPool-1-worker-2的i值:91
ForkJoinPool-1-worker-2的i值:92
ForkJoinPool-1-worker-2的i值:93
ForkJoinPool-1-worker-2的i值:94
......
ForkJoinPool-1-worker-2的i值:108
ForkJoinPool-1-worker-2的i值:109
ForkJoinPool-1-worker-2的i值:110
ForkJoinPool-1-worker-2的i值:111
ForkJoinPool-1-worker-1的i值:249
ForkJoinPool-1-worker-1的i值:250
ForkJoinPool-1-worker-1的i值:251
ForkJoinPool-1-worker-1的i值:252
ForkJoinPool-1-worker-1的i值:253

3 结果

从结果来看,程序启动了4个线程来执行这个打印任务——这是因为运行计算机有4个CPU。下面是CPU运行图。

ForkJoinPool介绍_第2张图片

三 返回结果集——通过多线程分多个小任务进行数据累加  

1 代码

import java.util.concurrent.*;
import java.util.*;

// 继承RecursiveTask来实现"可分解"的任务
class CalTask extends RecursiveTask
{
    // 每个“小任务”只最多只累加20个数
    private static final int THRESHOLD = 20;
    private int arr[];
    private int start;
    private int end;
    // 累加从start到end的数组元素
    public CalTask(int[] arr , int start, int end)
    {
        this.arr = arr;
        this.start = start;
        this.end = end;
    }
    @Override
    protected Integer compute()
    {
        int sum = 0;
        // 当end与start之间的差小于THRESHOLD时,开始进行实际累加
        if(end - start < THRESHOLD)
        {
            for (int i = start ; i < end ; i++ )
            {
                sum += arr[i];
            }
            return sum;
        }
        else
        {
            // 如果当end与start之间的差大于THRESHOLD时,即要累加的数超过20个时
            // 将大任务分解成两个小任务。
            int middle = (start + end) / 2;
            CalTask left = new CalTask(arr , start, middle);
            CalTask right = new CalTask(arr , middle, end);
            // 并行执行两个“小任务”
            left.fork();
            right.fork();
            // 把两个“小任务”累加的结果合并起来
            return left.join() + right.join();    // ①
        }
    }
}
public class Sum
{
    public static void main(String[] args)
        throws Exception
    {
        int[] arr = new int[100];
        Random rand = new Random();
        int total = 0;
        // 初始化100个数字元素
        for (int i = 0 , len = arr.length; i < len ; i++ )
        {
            int tmp = rand.nextInt(20);
            // 对数组元素赋值,并将数组元素的值添加到sum总和中。
            total += (arr[i] = tmp);
        }
        System.out.println(total);
        // 创建一个通用池
        ForkJoinPool pool = ForkJoinPool.commonPool();
        // 提交可分解的CalTask任务
        Future future = pool.submit(new CalTask(arr , 0 , arr.length));
        System.out.println(future.get());
        // 关闭线程池
        pool.shutdown();
    }
}

2 运行

917
917

3 说明

运行上面的程序,将可以看到通过CalTask计算出来的总和,与初始化数组元素时统计出来的总和总是相等的。

四 参考

https://blog.csdn.net/qq_27026603/article/details/82049741

你可能感兴趣的:(java)