fork-join框架简单的介绍

1 概述

当需要解决的问题可以表述为:

if (problem < SIZE) {
  // solve directly
}else{
  // divide the question
  // solve the sub question
  // solve the sub question
  // ...
  // join questions
}

就可以使用fork-join框架去解决.

fork-join具有 工作密取(work steal) 特性, 即每个线程会维护一个双端队列, 默认是从尾
部取任务, 当一个线程的工作队列为空时, 它会去其他线程的工作线程尾部窃取一个工作任务. 这种
机制可以让线程的限制时间减少, 提升程序效率, 并且减少获取工作任务的阻塞时间.

2 基本的使用方法

有两种方式去生命一个fork-join任务. 一种是RecursiveAction, 是不需要返回值的; 另一种是
RecursiveTask, T是它的返回值.

使用fork-join的步骤:
1. 集成RecursiveAction或RecursiveTask, 重写call方法;
2. 将RecursiveAtcion或RecursiveTask提交给ForkJoinPool的实例;

下面有一个简单的使用例子(引用自<

import java.util.concurrent.*;

/**
 * This program demonstrates the fork-join framework.
 * 程序功能:计算一个元素集里面多少个元素符合指定条件
 *  fork-in框架适合可以分割为更小问题的算法
 * @author Cay Horstmann
 * @version 1.00 2012-05-20
 */
public class ForkJoinTest {
    public static void main(String[] args) {
        final int SIZE = 10000000;
        double[] numbers = new double[SIZE];
        for (int i = 0; i < SIZE; i++){
          numbers[i] = Math.random();
        }

        // 开始计算
        Counter counter = new Counter(numbers, 0, numbers.length,
                new Filter() {
                    public boolean accept(double x) {
                        return x > 0.5;
                    }
                });
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(counter);
        System.out.println(counter.join());
    }
}

interface Filter {
    boolean accept(double t);
}

class Counter extends RecursiveTask {
    public static final int THRESHOLD = 1000;
    private double[] values;
    private int from;
    private int to;
    private Filter filter;

    public Counter(double[] values, int from, int to, Filter filter) {
        this.values = values;
        this.from = from;
        this.to = to;
        this.filter = filter;
    }

    protected Integer compute() {
        if (to - from < THRESHOLD) {
            //当元素个数小于THRESHOLD时, 直接计算
            int count = 0;
            for (int i = from; i < to; i++) {
                if (filter.accept(values[i])) count++;
            }
            return count;
        } else {
            //否则, 分割成两个子问题, 分别进行计算
            int mid = (from + to) / 2;
            Counter first = new Counter(values, from, mid, filter);
            Counter second = new Counter(values, mid, to, filter);
            //使用invokeAll来提交子任务
            invokeAll(first, second);
            //使用join来获取子任务的计算结果
            return first.join() + second.join();
        }
    }
}

注:
提交子任务的方法有invokeAll和fork. fork是开始一个子线程去执行这个任务, 而invokeAll则是
开始n-1个线程去执行任务, 但保留一个任务自己执行. 相对于fork方法, invokeAll更加节省线程
资源.

3 哪些东西使用了它

java 8 的stream框架的并行化stream的底层正是fork-join.

你可能感兴趣的:(java-多线程,并发,并发)