一、Fork-Join框架
forkjoin即分而治之,在几大排序算法中,快速排序、归并排序、二分查找均用到了分治的思想,即将一个大问题,逐一分解为一个个小问题,将各个子问题的解最终合并为最终解。这里也是如此,将一个大任务分解成若干个小任务(fork),再将这若干的小任务的执行结果进行合并(join)。
二、工作密取
fork-join采用工作密取的方式实现线程的工作负载。forkjoinPool中维护了多个线程执行分下的小任务,当当前线程的任务结束后,会自动获取其他线程的任务继续执行,同时也会根据当前工作线程的空闲情况去获取任务较多的线程的task,以达到线程间的平衡,提供CPU利用率。
三、fork-join的实现
forkjoin框架提供了两个子类供我们继承使用。
分别是:
1.RecursiveAction
此类主要用于没有返回值的任务。
2.RecursiveTask
此类主要用于有返回值的任务
两个类都提供了compute的方法,用于生成、调用子任务,完成分治运算。在compute方法中,需要判断当前任务是否达到设置的阈值,如果小于该值,则直接执行,否则,将该任务分割成两个小任务,再让两个小任务调用invokeAll方法,会再次进入compute方法,重复以上工作。最后使用join方法等到子任务执行结束获取其运行的结果,合并即可。
这里使用invokeAll而不是用invoke主要是因为,如果使用invoke会使任务分离开,两个任务中只工作一个任务,而使用invokeAll可以让两个任务同时工作,提供效率。
- fork-join的任务是通过ForkJoinPool池来执行,其提供了两种提交方式,submit和invoke,其中,submit是异步提交,不需要等待任务运行完毕即可运行sumbit之后的代码;相反,invoke是同步执行,必须等待任务完成才会执行invoke之后的代码。
四、使用Foke-Join实现数组求和
4.1创建求和任务类
自定义阈值,当任务低于该阈值时直接执行,否则进行分割,任务执行完毕后返回子任务合并的结果。
public class AddTask extends RecursiveTask<Integer> {
/**
* 阈值
*/
private static final int THRESHOLD=10;
private int[] nums;
//左边界
private int left;
//右边界
private int right;
public AddTask(int[] nums, int left, int right) {
this.nums = nums;
this.left = left;
this.right = right;
}
/**
* 生成随机数组
* @return
*/
public static int[] randomNum(){
Random r = new Random();
int[] nums = new int[100];
for(int i=0;i<100;i++){
nums[i] = r.nextInt(100);
}
return nums;
}
@Override
protected Integer compute() {
if (right-left<THRESHOLD){
//任务在阈值范围内,符合要求,执行执行任务
int sum=0;
for (int i = left; i <= right; i++) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
sum+=nums[i];
}
return sum;
}else {
//任务太大,需要分割成小任务
int mid=(left+right)/2;
AddTask leftTask=new AddTask(nums,left,mid);
AddTask rightTask=new AddTask(nums,mid+1,right);
invokeAll(leftTask,rightTask);
//返回子任务合并的结果
return leftTask.join()+rightTask.join();
}
}
}
4.2创建Add类调用任务类
*/
public class Add {
public static void main(String[] args) {
ForkJoinPool forkJoinPool=new ForkJoinPool();
int[] nums = AddTask.randomNum();
System.out.println("待求和数组:"+ Arrays.toString(nums));
//创建求和任务
AddTask addTask=new AddTask(nums,0,nums.length-1);
forkJoinPool.invoke(addTask);
System.out.println("求和中。。");
System.out.println("和为:"+addTask.join());
}
}
结果:
待求和数组:[34, 38, 71, 98, 38, 51, 35, 67, 59, 58, 33, 64, 65, 40, 30, 49, 67, 15, 4, 3, 5, 10, 94, 17, 24, 43, 94, 52, 53, 81, 7, 70, 25, 90, 96, 3, 84, 73, 54, 11, 42, 97, 18, 11, 95, 28, 14, 74, 95, 91, 53, 48, 69, 71, 91, 64, 25, 72, 3, 74, 77, 90, 22, 36, 77, 86, 41, 53, 92, 15, 30, 5, 51, 86, 60, 49, 76, 91, 15, 38, 55, 37, 24, 58, 53, 58, 75, 77, 34, 41, 88, 50, 91, 25, 56, 83, 14, 3, 75, 7]
求和中。。
和为:5134
而如果使用submit提交,将数组大小改为10000,就可看到求和中一句话是在任务执行完毕后才会打印,体现同步性。
一颗小陨石 发布了36 篇原创文章 · 获赞 7 · 访问量 2204 私信 关注