Fork/join-type thread pool and work-stealing algorithm

Source: Internet
Author: User

  

At JDK 1.7, the standard class library was added ForkJoinPool as an implementation of the Fork/join thread pool. Fork has a forked meaning in English, and Join has the meaning of merging . ForkJoinPoolthe same is true:fork forks large tasks into small tasks, then makes small tasks,joins is the result of getting small tasks, then merges, and merges the results as the result of a large task--and this is a recursive process-- Because the task is large enough, you can fork the task multilevel until the task is small enough.

Thus, it ForkJoinPool can satisfy the need of parallel implementation of the divide-and- Conquer algorithm (Divide-and-conquer) .

ForkJoinPoolThe class diagram is as follows:

You can see ForkJoinPool that the interface is implemented ExecutorService , so the first ForkJoinPool is a thread pool . Thus Runnable and Callable types of tasks ForkJoinPool can also be submit performed through, invokeAll and invokeAny other methods. But the standard class library also ForkJoinPool defines a new task, which is ForkJoinTask<V> .

ForkJoinTaskRelated class diagram:

ForkJoinTask<V>Used to define fork/join tasks--to complete the work of splitting large tasks into small tasks and merging results. In general, we do not need to inherit directly ForkJoinTask<V> , but inherit its subclass RecursiveAction and RecursiveTask implement the corresponding abstract method-- compute . RecursiveActionThis is a fork/join task with no return value, so using such a task does not produce results, it does not involve merging the results, but RecursiveTask is a fork/join task with a return value, and using such a task requires us to merge the results. By means of which fork we can produce subtasks and execute them join , we can get the results of subtasks by means of methods.

ForkJoinPoolThere are three ways to do this ForkJoinTask :

invokeMethod:

invokemethod is used to execute a task with a return value (usually inherited from RecursiveTask ), and the method is blocked until the task finishes executing, and the method stops blocking and returns the result of the task's execution.

submitMethod:

In addition to the ExecutorService inherited submit method, the method used to ForkJoinPool execute ForkJoinTask is defined-typically submit the submit method is used to execute a return value ForkJoinTask (usually inherited from RecursiveTask ). The method is non-blocking, and the task is returned immediately after the call ForkJoinPool to execute, and returns the task that has been committed to execution-the ForkJoinPool interface is realized by the class diagram ForkJoinTask Future , so it can be passed directly through the TAS K to interact with the submitted task.

executeMethod:

In addition to Executor the method obtained, the method execute ForkJoinPool used to execute is defined ForkJoinTask execute -Generally the execute method is used to execute without the return value ForkJoinTask (usually inherited from RecursiveAction ), the method is also non-blocking.

Now let's practice ForkJoinPool the function: Calculate the value of π.
The value of π is computed by a polynomial method, i.e.:
Π= 4 * (1-1/3 + 1/5-1/7 + 1/9-...)
The greater the number of items in the polynomial, the more accurate the calculated π value.

First we define what is used to estimate π PiEstimateTask :

StaticClass Piestimatetask extends Recursivetask<double> {Private finalLongBeginPrivate finalLongEndPrivate finalLong threshold;Critical value for split taskPublic Piestimatetask (LongBeginLongEndLong threshold) {This.Begin =BeginThis.End =EndThis.threshold = threshold; } @OverrideProtected Double Compute () {if (End-Begin <= Threshold) {int sign =1;Symbol, take 1 or-1Double result =0.0;for (Long i =Begin I <End i++) {result + = sign/(I *2.0 +1); sign =-sign; }return result *4; }Split tasklong middle = (begin + end)/ Span class= "Hljs-number" >2; Piestimatetask lefttask = new piestimatetask (begin, Middle, Threshold); Piestimatetask righttask = new piestimatetask (middle, end, Threshold); Lefttask.fork (); //asynchronously executes Lefttask righttask.fork (); //asynchronously executes righttask double Leftresult = LeftTask.join (); //blocked until Lefttask execution is complete return result double Rightresult = Righttask.join (); Span class= "Hljs-comment" >//blocked until Righttask execution has finished returning results return Leftresult + rightresult; Span class= "hljs-comment" >//Merge result}}            

Then we use ForkJoinPool the invoke execution PiEstimateTask :

PublicClass Forkjoinpooltest {public static void Main (String[] args) throws Exception {forkjoinpool forkjoinpool = new Forkjoinpool (4); //calculates 1 billion items, the threshold value of the split task is 10 million piestimatetask task = new piestimatetask (0, 1_000_000_000, 10_000_000); double pi = forkjoinpool.invoke (task); //blocked until the task has been executed to return the result System.out. println (the value of the  "π:" + pi); Forkjoinpool.shutdown (); //send closed instruction to thread pool}}            

Operation Result:

We can also use submit methods to execute tasks asynchronously (the submit object that the method returns in this case is the task that the task was submitted to):

PublicStaticvoid Main (string[] args)throws Exception {forkjoinpool forkjoinpool = new ForkJoinPool (4); Piestimatetask task = new piestimatetask ( 0, 1_000_000_000, 10_000_000); Future<double> future = Forkjoinpool.submit (task); Span class= "hljs-comment" >//does not block double pi = Future.get (); System.out. println ( "π value:" + pi); System.out. println ( "future points to the object is task:" + (future = = task)"); Forkjoinpool.shutdown (); //send closed instruction to thread pool}            

Operation Result:

It is important to note that selecting a critical value for a suitable partition task has a critical impact on the efficiency of the ForkJoinPool task. The threshold value is too large, the task segmentation is not thin enough, the CPU can not be fully utilized, the threshold is too small, then the task is too fragmented, may produce too many subtasks, resulting in excessive switching between threads and aggravating the burden of GC to affect the efficiency. Therefore, it is necessary to choose the critical value of a suitable partition task according to the actual application scenario.

ForkJoinPoolIn contrast ThreadPoolExecutor , there is a very important feature (the advantage) is the ForkJoinPool ability to have work-stealing (Work-stealing). The implementation of the so-called work-stealing ForkJoinPool is that each thread in the thread pool has a task queue that does not affect each other (a double-ended queue), and each thread takes a task out of the team header of its own task queue each time, and if a thread corresponds to a queue that is empty and in an idle state, While other threads have tasks in the queue that need to be processed but the thread is working, then the idle thread can take a task from the queue of the other thread to help run it-it feels like a free thread is going to steal someone's job to run, so it's called "work-stealing."

The work-stealing scenario is that different tasks are time-consuming, which means that some tasks need to run for a long time, and some tasks run quickly, in which case it is appropriate to use work-stealing, but if the task is time-consuming, then Work-stealing is not suitable, because stealing a task also requires a preemption lock, which can cause additional time consumption, and each thread maintains a double-ended queue that also causes greater memory consumption. So ForkJoinPool it is not ThreadPoolExecutor a substitute, but a ThreadPoolExecutor supplement to it.

Summarize:
ForkJoinPoolAnd ThreadPoolExecutor both are ExecutorService (thread pools), but ForkJoinPool the unique point is:

    1. ThreadPoolExecutorCan only execute Runnable and Callable task, ForkJoinPool not only can execute and task, but also can perform fork/join-------- Runnable Callable ForkJoinTask to meet the need of parallel implementation of divide and conquer algorithm;

    2. ThreadPoolExecutorThe order in which the tasks are executed is performed in the order in which they are in the Shared queue, so the subsequent tasks need to wait for the previous task to execute before they are executed, and ForkJoinPool each thread has its own task queue, and on this basis implements the Work-stealing function, which in some cases ForkJoinPoolcan increase the concurrency efficiency more greatly.

Fork/join-type thread pool and work-stealing algorithm

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.