On Java Fork / Join Framework for Parallel

Preliminary understanding Fork / Join framework

Fork / Join framework is a framework for parallel tasks in java7 added, the task can be divided into small enough small tasks, and then let the different threads to do these little things out of the division, and then to join after the completion of the small tasks results the results are assembled into large task. The image below shows the working model of such a framework:

 

 

Use premise Fork / Join our framework is parallel task can be split into small enough task, but the results can be assembled according to a result of a large task of small tasks, a simple example is the use Fork / Join framework to seek a the maximum / minimum values in the array, this task can be split into many small tasks, the big task is to find the maximum / minimum values of a large array, we can be split into a large array of many small arrays, respectively, and then solving each small the maximum / minimum values in the array, and then assembling the final results of these tasks the maximum and minimum, the following code shows how the maximum Fork / Join solving array:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
 
/**
* Created by hujian06 on 2017/9/28.
*
* fork/join demo
*/
public class ForkJoinDemo {
 
 /**
  * how to find the max number in array by Fork/Join
  */
 private static class MaxNumber extends RecursiveTask<Integer> {
 
   private int threshold = 2;
 
   private int[] array; // the data array
 
   private int index0 = 0;
   private int index1 = 0;
 
   public MaxNumber(int[] array, int index0, int index1) {
     this.array = array;
     this.index0 = index0;
     this.index1 = index1;
   }
 
   @Override
   protected Integer compute() {
     int max = Integer.MIN_VALUE;
     if ((index1 - index0) <= threshold) {
 
       for (int i = index0;i <= index1; i ++) {
         max = Math.max(max, array[i]);
       }
 
     } else {
       //fork/join
       int mid = index0 + (index1 - index0) / 2;
       MaxNumber lMax = new MaxNumber(array, index0, mid);
       MaxNumber rMax = new MaxNumber(array, mid + 1, index1);
 
       lMax.fork();
       rMax.fork();
 
       int lm = lMax.join();
       int rm = rMax.join();
 
       max = Math.max(lm, rm);
 
     }
 
     return max;
   }
 }
 
 public static void main(String ... args) throws ExecutionException, InterruptedException, TimeoutException {
 
   ForkJoinPool pool = new ForkJoinPool();
 
   int[] array = {100,400,200,90,80,300,600,10,20,-10,30,2000,1000};
 
   MaxNumber task = new MaxNumber(array, 0, array.length - 1);
 
   Future<Integer> future = pool.submit(task);
 
   System.out.println("Result:" + future.get(1, TimeUnit.SECONDS));
 
 }
 
}

It may be split into smaller tasks by setting different threshold values, the threshold value representative of the smaller split out more small tasks.

Work-stealing algorithm

Fork / Join in the realization, split out small tasks big task will be distributed to different queues inside, each queue will use a thread to the consumer, which is to get competition when multi-threaded task, but some thread He finished ahead of his own queue consumption. And some consumer queue thread is not finished in time, this time to complete the task thread will go steal those threads do not consume task queue, the thread in order to reduce competition, Fork / Join the use of double-ended queue to access small task, assignment to this queue thread would have been made from scratch and then perform a task, and the task steal thread is always pulled from the end of the queue.

Implementation details Frok / Join Framework

In the sample code above, we find that Fork / Join task is performed by ForkJoinPool, so the framework is a core task of the fork and join, then is this ForkJoinPool. Fork and join on the task, we can imagine, but also by our own code control, so to analyze Fork / Join, then the most worthy of study ForkJoinPool.

 

 

The image above shows a class diagram ForkJoinPool, you can see it is an Executor nature. In ForkJoinPool Inside, there are two particularly important members are as follows:

volatile WorkQueue[] workQueues; 
final ForkJoinWorkerThreadFactory factory;

workQueues to save the job submission to the ForkJoinPool, and specific execution has ForkJoinWorkerThread execution, and ForkJoinWorkerThreadFactory can be used to produce ForkJoinWorkerThread. You can see some of the ForkJoinWorkerThread, can be found in every ForkJoinWorkerThread have a pool and a workQueue, is consistent and we described above, each thread is assigned a task queue, perform this task queue thread is provided by the pool.

Here we look at when we when fork is what happened:

public final ForkJoinTask<V> fork() {
  Thread t;
  if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
    ((ForkJoinWorkerThread)t).workQueue.push(this);
  else
    ForkJoinPool.common.externalPush(this);
  return this;
}

See above fork the code, you can see the current thread to take first, and then determine whether it is our ForkJoinPool dedicated thread, and if so, the cast (down-converted) into ForkJoinWorkerThread, then the task will be to push this thread is responsible for the queue go inside. If the current thread is not ForkJoinWorkerThread type of thread, then logic would then go else, probably means that the first attempt to submit the task to the current thread, if unsuccessful, exception processing method is used on the underlying implementation is more complex, and we use Fork / Join not of so much, if you want to thoroughly understand the specific principles, you can see the source code.

The following look at the join process:

public final V join() {
   int s;
   if ((s = doJoin() & DONE_MASK) != NORMAL)
     reportException(s);
   return getRawResult();
 }
  
   private int doJoin() {
   int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
   return (s = status) < 0 ? s :
     ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
     (w = (wt = (ForkJoinWorkerThread)t).workQueue).
     tryUnpush(this) && (s = doExec()) < 0 ? s :
     wt.pool.awaitJoin(w, this, 0L) :
     externalAwaitDone();
 }
  
   final int doExec() {
   int s; boolean completed;
   if ((s = status) >= 0) {
     try {
       completed = exec();
     } catch (Throwable rex) {
       return setExceptionalCompletion(rex);
     }
     if (completed)
       s = setCompletion(NORMAL);
   }
   return s;
 }
  
  /**
  * Implements execution conventions for RecursiveTask.
  */
 protected final boolean exec() {
   result = compute();
   return true;
 }

The above shows the main call link, we found we finally fell written in code compute method, that is, to execute it, so we need to know is, fork just split the task, only when we execute the join our task will be executed amount.

How Fork / Join parallel frame

The foregoing shows a first array of maximum worth seeking examples, then introduces the "work-stealing algorithm", and then analyzed in some detail Fork / Join framework, here is what we are most concerned about how the use of Fork / Join framework it?

To use the Fork / Join framework, we only need to inherit the class RecursiveTask or RecursiveAction. The former is applicable to the scenario that returns a value, which is not suitable for return to the scene value.

https://mp.weixin.qq.com/s/vkvYJnKfQyuUeD_BDQy_1g

Get through the interview and other learning materials that can scan the next Fanger Wei code

Guess you like

Origin www.cnblogs.com/lemonrel/p/11699728.html
Recommended