Fork一词的原始含义是吃饭用的叉子,也有分叉的意思。在linux平台中,
方法fork用来创建子进程。使得系统进程可以多一个执行分组。
而join方法这里表示等待,也就是使用fork方法后系统多了一个执行分支(线程)
所以需要等待这个执行分支执行完毕,才有可能得到最终的结果。因此join方法
就表示等待。
毫无顾忌地使用fork方法开启线程处理,严重影响性能。JDK中,给出了一个ForkJoinPool
线程池。
由于线程池的优化,提交的任务和线程数量并不一一对应的关系。在绝大多数的情况下,
一个物理线程实际上需要处理多个逻辑任务的。因此,每个线程必然需要拥有一个任务队列。
因此在实际执行过程中,可能遇到一种情况:线程A已经把自己的任务都执行完了,而线程
B还有一大堆任务等着处理。
public class CountTask extends RecursiveTask<Long> {
private static final int THRESHOLD = 10000;
private long start;
private long end;
public CountTask(long start,long end){
this.start = start;
this.end = end;
}
protected Long compute() {
long sum = 0;
boolean canCompute = (end-start)<THRESHOLD;
if(canCompute){
for(long i=start;i<end;i++){
sum +=i;
}
} else {
long step =(start +end)/100;
ArrayList<CountTask> subTasks = new ArrayList<CountTask>();
long pos = start;
for(int i=0;i<100;i++){
long lastOne = pos+step;
if(lastOne>end) lastOne = end;
CountTask subTask = new CountTask(pos,lastOne);
pos+=step+1;
subTasks.add(subTask);
subTask.fork();
}
for(CountTask t:subTasks){
sum+= t.join();
}
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
CountTask task = new CountTask(0,200000L);
ForkJoinTask<Long> result = forkJoinPool.submit(task);
try{
long res = result.get();
System.out.println("sum="+res);
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
}
构造一个1到200 000求和的任务。
在ForkJoinTask<Long> result = forkJoinPool.submit(task);提交任务给线程池,
线程池会返回一个携带结果的任务,通过get()方法可以得到最终结果。
如果在执行get()方法时任务没有结束,那么主线程就会在get()方法时等待。
CountTask的实现,首先CountTask继承自RecursiveTask类,
可以携带返回值,这里的返回值类型设置为long类型,
THRESHOLD设置了任务分解的规模,也就是如果需要求和的总数大于
THRESHOLD个,那么任务就需要再次分解,否则
就可以直接执行。
如果和我可以直接执行,那么直接进行求和,返回结果。
否则,就对任务再次分解。每次分解时,简单地将原有任务分解成
100个等规模的小任务,并使用fork方法提交子任务。
之后,等待所有的子任务结束,
并将结果再次求和( for(CountTask t:subTasks){
sum+= t.join();
})