并行编程

  现在计算机大多已向多CPU方向发展,,为了充分利用多CPU、多核CPU的性能优势,计算机软件系统应该可以充分挖掘每个cpu的计算能力,为了充分利用多CPU、多核CPU的优势,可以考虑把一个任务拆分成多个小任务,把多个小任务放到多个处理器核心上并行执行;当多个小任务执行完成后,再将这些执行任务合并起来即可。

  JDK7引入了新的Fork/Join框架用于并行编程,从而利用多核处理器。一个问题分为不重叠的子问题,这些子问题可以并行地独立解决。然后合并所有子问题的结果获得问题的整体结果。这是分而治之方法的并行实现。一个分解(fork)可以看作运行在一个线程上的独立任务。

  框架使用ForkJoinTask类定义一个任务;同时,在一个ForkJoinPool的实例中执行一个任务。ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。

  ForkJoinTask是用于任务的抽象基类。一个ForkJoinTask是一个类似线程的实体,但是比普通的线程轻量级得多,因为巨量得任务和子任务可以被ForkJoinPool中得少数真正得线程所执行。任务主要使用fork()和join()来协调。在一个任务上调用fork()会安排异步得执行,然后调用join()等待任务完成。invoke()和invokeAll(tasks)方法都隐式地调用fork()来执行任务,以及join()等待任务完成,如果有结果则返回结果。

  Fork/Join框架是设计用于并行得分而治之解决方案,分而治之本身是递归得。RecursiveAction和RecursiveTask是ForkJoinTask的两个子类。要定义具体的任务类根据情况继承这两个子类。自己的任务重写compute()方法来指定任务是如何执行的。

  • RecursiveAction:定义不返回值的任务
  • RecursiveTask:定义返回值的任务
package edu.uestc.avatar;

import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ParallelMergeSort {
    /**
     * 分而治之:递归的
     * ForkJoinTask是用于任务的抽象类
     *         RecursiveAction:用于不返回值的任务
     *         RecursiveTask:用于返回值的任务
     * 任务是基于fork()和join()方法来协调
     *         fork():异步安排任务执行,然后调用join()等待任务完成
     *
     */
    private static class SortTask extends RecursiveAction{
        private static final long serialVersionUID = 1L;
        //终止递归分解的阈值
        private final int THRESHOLD = 500;
        private int[] list;
        
        public SortTask(int[] list) {
            this.list = list;
        }

        @Override //告诉任务完成什么
        protected void compute() {
            if(list.length < THRESHOLD)//终止递归条件:线性表较小的时候采用顺序解决方式效率更高
                Arrays.sort(list);
            else {
                int[] firstHalf = new int[list.length / 2];
                System.arraycopy(list, 0, firstHalf, 0, list.length / 2);
                
                int secondHalfLength = list.length - list.length / 2;
                int[] secondHalf = new int[secondHalfLength];
                System.arraycopy(list, list.length / 2, secondHalf, 0, secondHalfLength);
                
                /*
                 * invoke()/invokeAll():隐式地调用了fork()来执行任务,以及join()方法等待任务完成
                 * 通过invokeAll方法来调用子任务,该方法在所有子任务都完成后将返回。
                 * 注意:每个子任务又进一步递归分解为更小的子任务。巨量的子任务可以在池中创建和执行。Fork/Join框架高效地自动执行和协调所有的任务
                 */
                invokeAll(new SortTask(firstHalf),new SortTask(secondHalf));
                
                MergeSort.merge(secondHalf, firstHalf, list);
            } 
        }
    }
    
    /**
     * 程序创建一个RecursiveAction,一个ForkJoinPool,然后将该任务放在ForkJoinPool中执行
     * @param list
     */
    private static void parallelMergeSort(int[] list) {
        RecursiveAction task = new SortTask(list);
        ForkJoinPool pool = new ForkJoinPool();
        //invoke方法在主任务执行完后将返回
        pool.invoke(task);
    }
    public static void main(String[] args) {
        final int SIZE = 8000000;
        int[] list1 = new int[SIZE];
        int[] list2 = new int[SIZE];
        //初始化线性表也可以并行化(应该避免使用Math.random(),因为它是同步执行的,不可以并行执行)
        for(int i = 0; i < SIZE; i++)
            list1[i] = list2[i] = (int)(Math.random() * 100000000);
        
        long start = System.currentTimeMillis();
        MergeSort.mergeSort(list1);
        System.out.println("普通归并排序:" + (System.currentTimeMillis() - start) + "ms");
        
        start = System.currentTimeMillis();
        parallelMergeSort(list2);
        System.out.println("并行归并排序:" + (System.currentTimeMillis() - start) + "ms");
    }
}

  通过执行结果查看并行排序要比顺序排序快很多。

   示例:在线性表中查找最大数(有返回值)

package edu.uestc.avatar;

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

public class ParallelMax {
    public static void main(String[] args) {
        final int SIZE = 10000000;
        int[] list = new int[SIZE];
        for(int i = 0; i < SIZE; i++)
            list[i] = i;
        long start = System.currentTimeMillis();
        System.out.println("最大值为:" + max(list));
        System.out.println("并行处理器数量:" + Runtime.getRuntime().availableProcessors());
        System.out.println("花费时间:" + (System.currentTimeMillis() - start) + "ms");
    }
    
    private static int max(int[] list) {
        MaxTask task = new MaxTask(list,0,list.length);
        ForkJoinPool pool = new ForkJoinPool();
        return pool.invoke(task);//执行主任务
    }
    
    private static class MaxTask extends RecursiveTask<Integer>{
        /**
         * 
         */
        private static final long serialVersionUID = -3613298506153982577L;
        //终止递归分解的阈值
        private final int THRESHOLD = 1000;
        private int[] list;
        private int low;
        private int high;
        
        
        public MaxTask(int[] list, int low, int high) {
            this.list = list;
            this.low = low;
            this.high = high;
        }


        @Override
        protected Integer compute() {
            if(high - low < THRESHOLD) {
                int max = list[0];
                for(int i = low; i < high; i++) {
                    if(list[i] > max)
                        max = list[i];
                }
                return max;//自动装箱
            }else {
                int mid = (low + high) / 2;
                //分解成两个子任务,分别找到左边和右半边的最大元素
                RecursiveTask<Integer> left = new MaxTask(list, low, mid);
                RecursiveTask<Integer> right = new MaxTask(list, mid, high);
                
                //每个子任务又递归的分解成更多的子任务
                left.fork();
                right.fork();
                
                return Math.max(left.join(),right.join());
            } 
        }
    }
}

 

posted @ 2022-04-19 16:35  Tiger-Adan  阅读(539)  评论(0编辑  收藏  举报