Concurrent Programming Practice 10 - Detailed Explanation of ForkJoinPool Task Decomposition Mechanism in Multithreading

Fork/Join mode is similar to MapReduce, which is also equivalent to a divide and conquer concept, or like binary search and two-way merge algorithm. By decomposing a large number of computations into many small computations, dividing and conquering them, and then combining them, at the same time, each of these divided small computations is performed in parallel, thus greatly increasing the utilization of the CPU.

The Fork/Join pattern has its own scope. If an application can be decomposed into multiple subtasks, and the results of multiple subtasks can be combined to obtain the final answer, then this application is suitable for solving with the Fork/Join pattern. Figure 1 shows a schematic diagram of the Fork/Join mode. The Task at the top of the figure depends on the execution of the Task under it. Only when all subtasks are completed, the caller can get the return result of Task 0. It can be said that the Fork/Join pattern can solve many kinds of parallel problems. By using the Fork/Join framework provided by Doug Lea, software developers only need to focus on the division of tasks and the combination of intermediate results to take full advantage of the excellent performance of parallel platforms. Many other intractable problems related to parallelism, such as load balancing, synchronization, etc., can be solved by the framework in a unified manner. In this way, we can easily gain the benefits of parallelism without the difficult and error-prone disadvantages of parallel programming.
Figure 1. Schematic diagram of Fork/Join mode

In multi-threaded concurrent programming, there are sometimes scenarios where large tasks are decomposed into small tasks and then executed concurrently. Java 8's new ForkJoinPool supports this very well. ForkJoinPool is a thread pool that supports task decomposition. When the task submitted to him is "too large", he will decompose the large task into small tasks according to predefined rules, and execute them concurrently by multiple threads. It is generally used in conjunction with the decomposable task interface ForkJoinTask. ForkJoinTask has two abstract classes that implement it: RecursiveAction and RecursiveTask. The difference is that the former has no return value, and the latter has a return value.

  • Application scenario:
    In short: If your problem can be easily decomposed into sub-problems, ForkJoinPool is more suitable. Suitable for CPU-intensive scenarios.

  • Example: compute 1+2+…+100; parallel computing

package com.sound.daytc1;

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

/**
 * @author: ZouTai
 * @date: 2018/4/10
 * @description: ForkJoin模式计算序列相加-二分法
 */
public class RecursiveTaskDemo extends RecursiveTask<Integer> {
    private int first;
    private int last;

    public RecursiveTaskDemo(int first, int last) {
        this.first = first;
        this.last = last;
    }

    @Override
    protected Integer compute() {
        System.out.println(Thread.currentThread().getName() + " ... ");
        /**
         * 这里面要写自己的划分逻辑
         * 构造ForkJoin
         */
        int sum = 0;
        // 拆分任务
        if (last - first <= 2) {
            // 计算
            for (int i = first; i <= last; i++) {
                sum += i;
            }
        } else {
            /**
             * 类似于分支递归思想
             */
            RecursiveTaskDemo demo01 = new RecursiveTaskDemo(first, (last + first) / 2);
            RecursiveTaskDemo demo02 = new RecursiveTaskDemo((last + first) / 2 + 1, last);

            // 执行
            demo01.fork();
            demo02.fork();

            Integer a = demo01.join();
            Integer b = demo02.join();

            sum = a + b;
        }
        return sum;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(3);
        Future<Integer> future = forkJoinPool.submit(new RecursiveTaskDemo(1, 100));
        System.out.println("处理其他程序...");
        try {
            System.out.println("计算的值为:" + future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Fork/Join pattern in IBM-JDK 7

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326316998&siteId=291194637