Java并发编程--ForkJoin

概述

从JDK1.7开始,Java提供ForkJoin框架用于并行执行任务,它的核心思想就是分而治之,本文将详细介绍这个框架的使用。

 

什么是分而治之

分而治之的基本思想是:
将一个规模为N的问题分解为K个规模较小的子问题(K <= N),这些子问题相互独立且与原问题性质相同,求出子问题的解,就可以求出原问题的解。

分而治之也是递归的一种,一般的实现步骤为:
(1)设定阈值K
(2)不断将问题分解(或者说缩小规模),直到符合阈值K。
(3)按问题的要求,判断子问题的解,将子问题的解逐层合并构成原问题的解。

分而治之的重点:
看是否能够发现重复的子问题,能否发现大问题存在的循环子结构,如果发现就把原问题转化为很简单的小问题。
是否能划分步骤(不同步骤不同解决方法),因为单个步骤往往比整个问题解决起来要简单很多。
子问题是否很容易解决。

比如一个规模为n的问题,可以划分为1和 n-1两个部分,其中1是易于解决的。而n-1这个剩余部分可以用相同的划分方式分成1 , n-2两部分;重复这个过程,最终解决所有问题。也可以划分成n/2和n/2 两部分,然后对两个部分继续划分,最终都会成为一个1的简单问题。

常见的二分查找,二叉树等都采用了分而治之的思想。

 

ForkJoin使用的一般流程

// 1.初始化ForkJoin池
ForkJoinPool pool = new ForkJoinPool();

// 2.自定义任务实现
ForkJoinDemoTask task = new ForkJoinDemoTask();

// 3.执行任务
pool.invoke(task);

// 4.获取结果
task.join()

其中ForkJoinDemoTask是我们需要自定义实现的类,也是ForkJoin的核心。
ForkJoinDemoTask需要实现抽象类RecursiveTask、RecursiveAction、ForkJoinTask。核心方法是compute(),用于处理问题的核心业务和划分拆分子任务。

 

ForkJoin的同步调用Demo

计算1-1000的数累加,其中单个累加模拟10ms的业务处理,通过传统单线程和ForkJoin分别实现。

public class ForkJoinTest {

    /**
     * 核心类
     */
    public static class ForkJoinTask extends RecursiveTask {

        // 阈值size
        private final static int size = 10;

        private int[] src;
        private int from;
        private int to;

        public ForkJoinTask(int[] src, int from, int to) {
            this.src = src;
            this.from = from;
            this.to = to;
        }

        @Override
        protected Integer compute() {
            if(to - from <= size) {
                // 规模为N的问题,N<阈值,直接解决
                int count = 0;
                for(int i = from; i <= to; i++) {
                    try {
                        // 延时10ms 模拟业务处理
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count += src[i];
                }

                return count;
            } else {
                // N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解
                int mid = (from + to) / 2;
                ForkJoinTask left = new ForkJoinTask(src, from, mid);
                ForkJoinTask right = new ForkJoinTask(src, mid + 1, to);
                invokeAll(left,right);
                return left.join() + right.join();
            }
        }
    }

    /**
     * ForkJoin实现
     */
    public static void doForkJoin(int[] src) {
        long start = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask task = new ForkJoinTask(src, 0, 999);
        pool.invoke(task);
        System.out.println("The res is "+ task.join()
                +" doForkJoin time:"+(System.currentTimeMillis()-start)+"ms");
    }

    /**
     * 传统单线程实现
     */
    public static void doNormal(int[] src) {
        long start = System.currentTimeMillis();

        int count = 0;
        for(int i=0; i<1000;i++) {
            try {
                // 延时10ms 模拟业务处理
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count+= src[i];
        }

        System.out.println("The res is "+ count
                +" doNormal time:"+(System.currentTimeMillis()-start)+"ms");
    }

    public static void main(String[] args) {
        int src[] = new int[1000];
        for(int i=0;i<1000;i++){
            src[i] =  i + 1;
        }

        doForkJoin(src);
        doNormal(src);
    }
}

ForkJoin的异步调用Demo

遍历指定目录(含子目录)找寻指定类型文件。

public class FileTask extends RecursiveAction{

    private File path;//当前任务需要搜寻的目录

    public FileTask(File path) {
        this.path = path;
    }

    public static void main(String [] args){
        try {
            // 用一个 ForkJoinPool 实例调度总任务
            ForkJoinPool pool = new ForkJoinPool();
            FileTask task = new FileTask(new File("D:/"));

            pool.execute(task);//异步调用

            System.out.println("Start......");
            Thread.sleep(1);
            int count = 0;
            for(int i = 0; i < 100; i++){
                count = count + i;
            }
            System.out.println("Main Thread doing......, count = " + count);
            task.join();//阻塞的方法
            System.out.println("End......");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void compute() {
        
        List subTasks = new ArrayList<>();
        
        File[] files = path.listFiles();
        if(files != null) {
            for(File file : files) {
                if(file.isDirectory()) {
                    subTasks.add(new FileTask(file));
                }else {
                    //检查
                    if(file.getAbsolutePath().endsWith("txt")) {
                        System.out.println("文件:"+file.getAbsolutePath());
                    }
                }
            }
            if(!subTasks.isEmpty()) {
                for(FileTask subTask:invokeAll(subTasks)) {
                    subTask.join();//等待子任务执行完成
                }
            }
        }
    }
}

 

你可能感兴趣的:(Java笔记)