Java高效并发(十)----ForkJoin框架

分而治之策略

当我们要处理很大的数据,一个重要的思想就是把问题划分成若干个小问题处理,然后把小问题的结果进行整合,得到最终的结果。在JDK中有一个ForkJoin线程池,使用fork/join方法处理数据。Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决

ForkJoinPool线程池

java.util.concurrent.ForkJoinPool 继承了 AbstractExecutorService类,这个线程池提供了下面几种执行任务的方法,有些有返回值有些没有返回值。有些要求任务是Runnable对象,有些要求任务是Callable对象

 它的无参数构造方法,返回跟系统CPU数量一样的线程

 public ForkJoinPool() {
        this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
             defaultForkJoinWorkerThreadFactory, null, false);
    }

 其中一个重要的方法是,提交ForkJoinTask对象的任务,返回ForkJoinTask对象的结果 

 public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task;
    }

 ForkJoinTask任务

 ForkJoinTask任务就是支持fork()分解和join()等待的任务。

ForkJoinTask是一个J.U.C下的一个抽象类

public abstract class ForkJoinTask<V> implements Future<V>, Serializable

这个类中有两个方法

fork()方法,当ForkJoinTask任务执行fork()方法,会把ForkJoinTask任务提交给ForkJoinPool

join方法,ForkJoinTask任务执行join()方法,会把结果返回。

它有两个实现类

①  public abstract class RecursiveTask<V> extends ForkJoinTask<V>  带有返回结果的任务

②  public abstract class RecursiveAction extends ForkJoinTask<Void>  没有返回结果的任务

 通常我们并不直接继承 ForkJoinTask,它包含了太多的抽象方法。针对特定的问题,我们可以选择 ForkJoinTask 的不同子类来完成任务。RecursiveAction 是 ForkJoinTask 的一个子类,它代表了一类最简单的 ForkJoinTask:不需要返回值,当子任务都执行完毕之后,不需要进行中间结果的组合。如果我们从 RecursiveAction 开始继承,那么我们只需要重载 protected void compute() 方法

RecursiveTask 是 ForkJoinTask 的一个子类,它是需要返回值的任务,当子任务都执行完毕之后,进行中间结果的组合。如果我们从RecursiveTask 开始继承,那么我们只需要重载 protected void compute() 方法,它可使用泛型指定一个返回值的类型

 下面这个例子解决这样一个问题就用到带有返回结果的任务。

如何充分利用多核CPU,计算很大List中所有整数的和?

那一个大的任务分成几个小任务来执行。而且在ForkJoinPool线程池中,每一个线程对应一个任务队列,如果线程A已经完成了自己的任务,发现B线程的任务队列还有很多,它会去B的任务队列从后面拿任务来执行,如果任务时ForkJoinTask类型的,就支持join(),一起返回。

package xidian.lili.reentrantlock;

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;

public class CountTask extends RecursiveTask<Long>{
	public int start;
	public int end;
	public final int THROLDSOLD=1000;
	public CountTask(int start, int end) {
		this.start = start;
		this.end = end;
	}
	protected Long compute() {
		Long sum=0L;
		if((end-start)<THROLDSOLD){
			for(int i=start;i<=end;i++){
				sum+=i;
			}
		}else{
			int step=(start+end)/100;
			ArrayList<CountTask> subtasks=new ArrayList();
			int pos=start;
			for(int i=0;i<100;i++){
				int lastone=pos+step;
				if(lastone>end)lastone=end;
				CountTask subtask=new CountTask(pos,lastone);
				subtasks.add(subtask);
				pos=lastone+1;
				subtask.fork();				
			}
			for(CountTask subtask:subtasks){
				sum+=subtask.join();
			}
		}
		return sum;
	}	
	public static void main(String[] args) {
		CountTask task=new CountTask(0,10000);
		ForkJoinPool pool=new ForkJoinPool();
		ForkJoinTask<Long> result=pool.submit(task);
		try {
			long res=result.get();
			System.out.println(res);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			
			e.printStackTrace();
		}

	}

}

 问题

使用Fork/Join模式,如果任务的层次划分的很深,一直得不到返回值,一可能就是任务太多,系统内的线程数多,导致系统性能下降。二可能调用层次太深导致栈溢出

当我们上述任务的子任务划分更小,也就是调用层次更深,出现了OOM异常

 

猜你喜欢

转载自blog.csdn.net/wangdongli_1993/article/details/81271570