Jdk:fork-join Frame

Source: Internet
Author: User
Tags throwable
Overview

The previous article "JDK": The executor framework refers to the executor framework, while the Fork-join framework also relates to the executor framework for multithreaded parallel operations.

The Fork-join framework has its own scope of application. If an application can be decomposed into multiple subtasks, and the result of combining multiple sub-tasks can get the final answer, then this application is suitable to be solved with fork-join framework pattern. The following figure shows a schematic diagram of a fork-join frame pattern in which the task at the top of the graph relies on the execution of the task under it, and only when all the subtasks are complete can the caller get the return result of task 0.

Fork-join framework can solve many kinds of parallel problems. Software developers only need to focus on the partitioning of tasks and the combination of intermediate results to take advantage of the good performance of the parallel platform. Other problems that are difficult to deal with in parallel, such as load balancing, synchronization, etc., can be resolved in a unified manner by the framework.

The Fork-join framework is a concrete implementation of the Executorservice interface, which is designed to make better use of multiprocessor. It is designed for those types of work that can be disassembled recursively into subtasks. Similar to other implementations of the Executorservice interface, the Fork-join framework distributes tasks to worker threads in the thread pool.

The main class diagrams and inheritance relationships of the Fork-join framework are as follows:


The core of the Fork-join framework is the Forkjoinpool class, which is an extension of the Abstractexecutorservice class. Forkjoinpool implements a work-stealing algorithm and can perform forkjointask tasks. work-Stealing algorithms

The Fork-join framework reduces contention for work queues through a technique called work-stealing (job stealing). Each worker thread has its own work queue, which is implemented using a double-ended queue (or deque) (Java 6 adds several deque implementations in the class library, including Arraydeque and Linkedblockingdeque). When a task divides a new thread, it pushes itself to the head of the deque. When one task performs a merge operation with another unfinished task, it pushes another task to the head of the queue and executes without sleeping to wait for the other task to complete (like the Thread.Join () operation). When the thread's task queue is empty, it tries to steal another task from the tail of another thread's deque.

Work-stealing can be achieved using standard queues, but Deque has two advantages over standard queues: reduced contention and theft. Because only the work line routines accesses the head of its own deque, the Deque head will never compete, because only when a thread is idle will it be able to access the tail of deque, so there is very little contention for the deque tail of the thread (in the Fork-join framework, combining the deque implementation will make this Further reduce coordination costs). Reducing contention can significantly reduce synchronization costs compared to traditional thread-pool-based approaches. In addition, this method implies that the last-in-first-out (LAST-IN-FIRST-OUT,LIFO) Task queueing mechanism means that the largest task is queued at the end of the queue, and when another thread needs to steal a task, it will be able to break down into tasks that can be broken down into smaller tasks, thus avoiding future thefts. As a result, work-stealing achieves a reasonable load balance, eliminating the need for coordination and minimizing synchronization costs. Core Steps

The core of Fork-join framework consists of two steps of dividing task and merging task result.

The first step is to split the task. First we need to have a fork class to divide the large task into sub-tasks, it is possible that the subtasks are still very large, so it is necessary to continue to split, until the split sub-task is small enough.

The second step executes the task and merges the results. Split subtasks are placed in a double-ended queue, and several boot threads get task execution from the double-ended queue, respectively. The results of the subtasks are uniformly placed in a queue, starting a thread to take the data from the queue, and then merging the data.

Fork-join uses two classes to accomplish both of these things (see the class diagram above): Forkjointask: To use the Forkjoin framework, you must first create a forkjoin task. It provides a mechanism for executing the Fork () and join () operations in a task, and typically we do not need to inherit the Forkjointask class directly, but only inherit its subclasses, the Fork/join framework provides the following two subclasses:
Recursiveaction: Used for tasks that do not return results. Recursivetask: Used for tasks that have return results. The forkjoinpool:forkjointask needs to be driven by Forkjoinpool, and the subtasks are added to the double-ended queue maintained by the current worker thread and into the head of the queue. When there is no task in the queue for a worker thread, it randomly fetches a task from the end of the queue of other worker threads. part of the source

The forkjoinpool consists of an array of Forkjointask and Forkjoinworkerthread, and the Forkjointask array is responsible for storing the tasks that the program submits to Forkjoinpool. The Forkjoinworkerthread array is responsible for performing these tasks. Fork of Forkjointask ()

When we call Forkjointask's Fork method, the program calls Forkjoinworkerthread's Pushtask method to perform the task asynchronously, and then returns the result immediately. The code is as follows:

Public final Forkjointask Fork () {(          
    (forkjoinworkerthread) Thread.CurrentThread ()). Pushtask (this);         
    return this; 

The Pushtask method stores the current task in the Forkjointask array queue. Then call Forkjoinpool's Signalwork () method to wake up or create a worker thread to perform the task. The code is as follows:

    final void Pushtask (Forkjointask t) {
        forkjointask[] q; int s, M;
        if ((q = queue) = null) {    //Ignore if queue removed
            long u = (((s = queuetop) & (M = q.length-1) << Ashift) + abase;
            Unsafe.putorderedobject (q, U, t);
            Queuetop = s + 1;         or use Putorderedint
            if ((S-= queuebase) <= 2)
                pool.signalwork ();
            else if (s = = m)
                growqueue ();
        }
    }
Join of Forkjointask ()

The primary role of the Join method is to block the current thread and wait for the results to be obtained. The code is as follows:

Public final V join () {
        if (dojoin ()! = NORMAL)
            return Reportresult ();
        else
            return Getrawresult ();
}

Private V Reportresult () {
        int s; Throwable ex;
        if ((s = status) = = CANCELLED)
            throw new Cancellationexception ();
        if (s = = Exceptional && (ex = Getthrowableexception ()) = null)
                    unsafe.throwexception (ex);
                return Getrawresult ();
        }

First, it calls the Dojoin () method, using the Dojoin () method to get the status of the current task to determine what results are returned, the task status of four:
Completed (NORMAL), Canceled (CANCELLED), Signal (SIGNAL), and an exception occurred (exceptional). If the task status is completed, the task results are returned directly. If the task status is canceled, the cancellationexception is thrown directly. If the task status throws an exception, the corresponding exception is thrown directly.

Implementation code for the Dojoin () method:

private int Dojoin () {
        Thread t; 
        Forkjoinworkerthread W; 
        int s; 
        Boolean completed;
        if ((t = Thread.CurrentThread ()) instanceof Forkjoinworkerthread) {
            if ((s = status) < 0)
                return s;
            if ((w = (forkjoinworkerthread) t). Unpushtask (This)) {
                try {
                    completed = EXEC ()
                } catch (Throwable Rex) {
  return Setexceptionalcompletion (Rex);
                }
                if (completed)
                    return setcompletion (NORMAL);
            }
            Return W.jointask (this);
        }
        else
            return Externalawaitdone ();
    }

In the Dojoin () method, first by looking at the status of the task, see whether the task has been completed, if done, then directly return to the task status, if not completed, then remove the task from the task array and execute. If the task completes successfully, the task state is set to normal, and if an exception occurs, the exception is logged and the task status is set to exceptional. Reference and recommended reading

The following articles refer to examples of how to use fork-join, such as getting the maximum value, the number accumulation, the Fibonacci sequence, the merge sort, and so on. Take a leap, talk about concurrency (eight)--fork/join framework introduces Fork/join mode Java theory and practice in JDK 7: Application Fork-join Framework

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.