What is the fork/join

The Fork/Join framework is a framework for executing concurrent tasks provided by JDK1.7. Developers can write good multi-threaded tasks by following the Fork/Join development mode without knowing about Thread, Runnable and other related knowledge.

At the same time, according to the idea of divide-and-rule, large tasks can be divided into several tasks, and the results of small tasks can be summarized to obtain the framework of large task results.

The understanding of Fork/Join framework can be considered as consisting of two parts. Fork is to cut a large task into several small tasks for concurrent execution, and Join is to merge the result of these small tasks and finally obtain the result of the large task.

Job stealing algorithm

If all tasks of the current thread are completed, the system automatically obtains the Task from the Task pool of another thread and continues to execute the Task. ForkJoinPool maintains multiple threads (usually CPU cores) that execute tasks continuously. Each thread performs its own task list and obtains tasks from other busy threads based on the idle status of its own worker thread. This reduces the amount of time that a thread is blocked or idle. Improve CPU utilization.

The use of Fork/Join

The basic concept

To use Fork/Join, you first need a Pool. It can be used to perform tasks. ForkJoinTask Each task is called ForkJoinTask and has an improved internal mechanism for forking and joining. Generally, developers do not need to inherit ForkJoinTask directly, but instead inherit subclasses of ForkJoinTask as follows:

  • RecursiveAction: Returns a task with no result
  • RecursiveTask: Returns a result task

  1. New ForkJoinPool
  2. New ForkJoinTask (RecursiveAction or RecursiveTask)
  3. In the compute method within a task, the task is split based on custom criteria. If the criteria are met, the task is split. If the criteria are not met, the task is split. When all tasks are completed, summarize the final results.
  4. The result is eventually obtained via join or GET

The synchronization result is returned

Requirement: Count the sum of all elements in an integer array

Public class GenArray {public static final int ARRAY_LENGTH=400000; public static int[] genArray(){ Random random = new Random(); int[] result = new int[ARRAY_LENGTH]; for (int i = 0; i < ARRAY_LENGTH; I ++) {result[I]= random.nextint (ARRAY_LENGTH*3); } return result; }}Copy the code
Public class SumNormal {public static void main(String[] args) {int count = 0; int[] src = GenArray.genArray(); long start = System.currentTimeMillis(); for (int i = 0; i < src.length; i++) { count+=src[i]; } System.out.println("spend time: "+(System.currentTimeMillis()-start)); }}Copy the code
Public class forkJoin {private static class SumTask extends RecursiveTask<Integer> {private final static int THRESHOLD=GenArray.ARRAY_LENGTH/10; private int[] src; private int fromIndex; private int endIndex; public SumTask(int[] src, int fromIndex, int endIndex) { this.src = src; this.fromIndex = fromIndex; this.endIndex = endIndex; } @override protected Integer compute() {if (endindex-fromindex <THRESHOLD){int count = 0; for (int i = fromIndex; i <= endIndex; i++) { count+=src[i]; } return count; Int mid = (fromIndex+endIndex)/2; int mid = (fromIndex+endIndex)/2; SumTask left = new SumTask(src,fromIndex,mid); SumTask right = new SumTask(src,mid+1,endIndex); invokeAll(left,right); return left.join()+right.join(); } } } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); int[] src = GenArray.genArray(); SumTask sumTask = new SumTask(src,0,src.length-1); long start = System.currentTimeMillis(); pool.invoke(sumTask); System.out.println("spend time: "+(System.currentTimeMillis()-start)); }}Copy the code

Based on the results of the execution, it is more efficient to use a normal loop with a small amount of data because it is added internally as a bus. Fork /join takes advantage of the number of CPU cores currently available in conjunction with the context switch of the thread, so there is a performance cost, but forkJoin is significantly more efficient than a regular for loop if the data volume is large

Asynchronous no result value is returned

Requirements: traverse directories (including subdirectories) to find TXT files

public class FindFile extends RecursiveAction { private File path; public FindFile(File path) { this.path = path; } @Override protected void compute() { List<FindFile> takes = new ArrayList<>(); File[] files = path.listfiles (); if (files ! = null){for (File File: files) {if (file.isdirectory ()){// Recursive call takes.add(new FindFile(File)); }else {// not a folder. Check if (file.getabsolutePath ().endswith (" TXT ")){system.out.println (file.getabsolutePath ()); }}} // Schedule all subtasks to execute if (! // block the current thread and wait for the result task.join(); } } } } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); FindFile task = new FindFile(new File("F://")); pool.submit(task); // the main thread join waits for the subtask to complete. task.join(); System.out.println("task end"); }}Copy the code