This article will start with a simple example and explain why ForkJoinPool exists.

Then introduce the basic information and use of ForkJoinPool, and finally explain the basic principles of ForkJoinPool.

Reason for birth

For thread pools, we often use ThreadPoolExecutor, which can be used to improve task processing efficiency. In general, when we use ThreadPoolExecutor, there is no connection between tasks. But in some special cases, there are connections between the tasks we work on, such as the classic Fibonacci algorithm.

For the Fibonacci sequence, we know that F(N) = F(N-1) + F (N-2). The results of the current value depend on the results of the next few values. At this point, if you use ThreadPoolExecutor, it seems that you will not be able to solve the problem. Although we can have a single-threaded recursive algorithm, its computation speed is slower, and it cannot be done in parallel, which cannot take advantage of the multi-core CPU core.

ForkJoinPool is designed to solve the problem of parallel computing on which parent-child tasks depend. Parallel computing problems such as quick sorting, binary lookup, and set operations that have parent-child dependencies can be solved with ForkJoinPool. For the Fibonacci sequence problem, if it is implemented with ForkJoinPool, the implementation code is:

@Slf4jpublic class ForkJoinDemo { // 1. run the portal public static void main(String[] args) { int n = 20; To keep track of child thread names, you need to override the method of ForkJoinWorkerThreadFactory final ForkJoinPool.ForkJoinWorkerThreadFactory factory = pool -> { final ForkJoinWorkerThread worker = ForkJoinPool. defaultForkJoinWorkerThreadFactory.newThread(pool); worker.setName(“my-thread” + worker.getPoolIndex()); return worker;        }; To create a partition task thread pool, you can trace the thread name to ForkJoinPool forkJoinPool = new ForkJoinPool(4, factory, null, false); Quickly create ForkJoinPool methods // ForkJoinPool forkJoinPool = new ForkJoinPool(4); Create partition task Fibonacci fibonacci = new Fibonacci(n); Call the invoke method to start the partition task Integer result = forkJoinPool.invoke(fibonacci); (the result of “Fibonacci {} is {}”, n, result); }}// 2. Define the split task, write the split logic@Slf4jclass Fibonacci extends RecursiveTask { final int n; Fibonacci(int n) { this.n = n;    } @Override public Integer compute() { // and recursion similar, defines the smallest unit that can be computed if (n <= 1) { return n; } // If you want to see the output of the sub-thread name, you can open the following comment // (Thread.currentThread().getName()); Fibonacci f1 = new Fibonacci(n - 1); Split into subtasks f1.fork(); Fibonacci f2 = new Fibonacci(n - 2); f1.join Wait for the subtask execution result return f2.compute() + f1.join(); }}

As shown in the code above, we define a Fibonacci class that inherits the RecursiveTask abstract class. In the Fibonacci class, we define the splitting logic and call join() to wait for the child thread to execute the result. Run the program and we will get the following result:

17:29:10.336 [main] INFO tech.shuyi.javacodechip.forkjoinpool.ForkJoinDemo – The result of Fibonacci 20 is 6765

The fork() and join () mentioned in the preceding code are API interfaces provided by ForkJoinPool that are mainly used to execute tasks and wait for child thread results. As for its detailed usage, we will talk about it later.

In addition to handling situations where parent-child tasks are dependent, ForkJoinPool can also be used to handle scenarios where you need to get the results of subtask execution. For example, if we want to calculate the sum of 100 million to 100 million, in order to speed up the calculation, we naturally think of the principle of partition in the algorithm, dividing 100 million numbers into 10,000 tasks, and calculating the synthesis of 10,000 values for each task, using the concurrent computing performance of the CPU to shorten the calculation time.

Because ThreadPoolExecutor can also get execution results through Future, ThreadPoolExecutor is also possible. At this time, we have two implementations, one with ThreadPoolExecutor and the other with ForkJoinPool. Let’s implement both of these ways and see how the two implementations differ.

Regardless of the implementation method, the general idea is:

That’s to avoid conflict! If two threads get tasks from the top at the same time, there will be a conflict problem of multiple threads, and a lock operation will be required, which will reduce the execution efficiency.

Welcome to the programmer exchange group

Ghost brother I created several technical exchange groups, everyone in the group are nine points to talk about technology, one point to talk about the wind and snow ~

If you don’t add a group, you may wish to scan the QR code below and add my WeChat. I pull everyone into the group, remember the note: work city + nickname + technical direction!

Ps: If the circle of friends sets permissions on me, then you don’t need to add my friends.

Past Recommendations

What are the state-owned enterprises that deserve to be joined by computer majors?

What is it like for a programmer to have a product manager girlfriend?

I used last year’s resume to vote for all the companies that got the offer, and the result was a mixture of feelings…

A new technical director, call RabbitMQ a thorough, admire!

Small teams can also do DDD-Novella