• 【java并发核心八】Fork-Join分治编程


    jdk1.7中提供了Fork/Join并行执行任务框架,主要作用就是把大任务分割成若干个小任务,再对每个小任务得到的结果进行汇总。

    正常情况下,一些小任务我们可以使用单线程递归来实现,但是如果要想充分利用CPU资源,就需要把一个任务分成若干个小任务,并行执行了,这就是分治编程。

    在JDK中,并行执行框架Fork-Join使用了“工作窃取(work-stealing)”算法。

    JDK1.7中实现分治编程思路:

      使用 ForkJoinPool 类提供了一个任务池。

      具体执行任务需要靠 ForkJoinTask 类,而 ForkJoinTask 是抽象类,故使用该类的3个子类 CountedCompleter、RecursiveAction、RecursiveTask 来实现具体的功能。

      其中,RecursiveAction 执行的任务具有无返回值,且仅执行一次;RecursiveTask 执行任务可以通过方法 join() 或者 get() 取得方法返回值。

      补充, join() 和 get() 的区别:两个方法都可以获得计算后的结果值,区别是在子任务报异常时,get() 的异常可以在main主线程中进行捕获;而 join() 的异常会直接抛出。

    看例子(求和:1+2+3+...+9999+10000):

    package com.cd.thread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    import java.util.concurrent.RecursiveTask;
    
    public class ForkJoinTest {
        public static void main(String[] args) {
            final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            System.out.println("start-time:" + sf.format(new Date()));
            addTest(); // for循环
            System.out.println("end-time:" + sf.format(new Date()));
            System.out.println("start-time:" + sf.format(new Date()));
            forkJoinTest();// 分治求和
            System.out.println("end-time:" + sf.format(new Date()));
        }
    
        private static void addTest() {
            int beginNum = 1, endNum = 10000, val = 0;
            for (int i = beginNum; i <= endNum; i++) {
                val += i;
            }
            System.out.println("for循环结果:" + val);
        }
    
        public static void forkJoinTest() {
            MyRecursiveTask task = new MyRecursiveTask(1, 10000);
            ForkJoinPool pool = new ForkJoinPool();
            ForkJoinTask<Integer> fjTask = pool.submit(task);
            try {
                System.out.println("分治求和结果:" + fjTask.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static class MyRecursiveTask extends RecursiveTask<Integer> {
    
            private Integer beginNum;
    
            private Integer endNum;
    
            private MyRecursiveTask(Integer beginNum, Integer endNum) {
                this.beginNum = beginNum;
                this.endNum = endNum;
            }
    
            @Override
            protected Integer compute() {
                if ((endNum - beginNum) > 500) {
                    int middleNum = (endNum + beginNum) / 2;
                    MyRecursiveTask task1 = new MyRecursiveTask(beginNum, middleNum);
                    MyRecursiveTask task2 = new MyRecursiveTask(middleNum + 1, endNum);
                    this.invokeAll(task1, task2);
                    //                return task1.join() + task2.join();
                    Integer num1 = 0, num2 = 0;
                    try {
                        num1 = task1.get();
                        num2 = task2.get();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return num1 + num2;
                } else {
                    Integer val = 0;
                    for (int i = beginNum; i <= endNum; i++) {
                        val += i;
                    }
                    return val;
                }
            }
    
        }
    
    }

    从结果看,分治编程不一定会比单线程快,所以在用分治编程的时候,需要一定的测试才行。

    而分治编程也有运用的领域,比如遍历一个目录及其子目录,处理一个树形结构算法问题。

    在写代码的时候,会发现分治的代码看起来像递归,但是其实它们是并行执行的。

    关于 ForkJoinPool 的 api,建议用到的时候,去看文档吧,看文档也是一种能力,也是一种技巧。

  • 相关阅读:
    nodejs + typescirpt + vs code
    NodeJs使用nodejs-websocket + protobuf
    Windows10环境下使用VisualSVN server搭建SVN服务器
    微信小游戏下socket.io的使用
    JS中实现种子随机数
    帧同步和状态同步
    EgretPaper学习笔记一 (安装环境,新建项目)
    反编译微信小游戏
    微信小游戏 小程序跳转修改 不支持动态更新,只能在发布时修改
    HTML5实现本地JSON文件的读写
  • 原文地址:https://www.cnblogs.com/klbc/p/9797969.html
Copyright © 2020-2023  润新知