开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
本文主要介绍并发中的ForkJoin和异步回调,ForkJoin会将任务拆分去做以提高效率,异步回调学过前端的朋友都应该很清楚。
14. ForkJoin
ForkJoin是在JDK1.7中出现的。并行执行任务!来提高效率。在大数据量大情况下它比较好。其实它做的一件事情就是将大任务划分成小任务去执行,最后合并。
大数据:Map Reduce(把大任务拆分成小任务)。
ForkJoin特点:工作窃取。 就好比A、B两个线程去执行任务,B线程执行完了,B不会等待,会将A的任务拿过来一部分去执行。这里面维护的都是双端队列。
ForkJoin的操作
- 第一步:新建ForkJoinPool,用ForkJoinPool来执行任务(我们用的是submit)。
- 第二步:写一个继承ForkJoinTask的子类(也可以继承它的籽类),ForkJoinTask已知的子类:RecursiveTask(递归任务,有返回值的)或RecursiveAction(递归事件,无返回值的),函数内容写到compute方法。
- 第三步:提交任务。
- fork是拆分任务的方法,join是合并结果的方法。
public class ForkJoinDemo extends RecursiveTask<Long> {
private long start;
private long end;
//临界值
private long temp = 10000L;
public ForkJoinDemo(long start, long end) {
this.start = start;
this.end = end;
}
//计算方法
@Override
protected Long compute() {
//超过临界值
if((end - start) < temp) {
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;//基本求和
}else {//forkjoin 像递归
long middle = (start + end) / 2;//中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
task1.fork();//拆分任务,把任务压入线程队列
task2.fork();//拆分任务,把任务压入线程队列
return task1.join() + task2.join();//任务结果合并
}
}
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinDemo task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务(有结果,可以拿到)
// forkJoinPool.execute(task);//执行任务(没有结果)
Long sum = submit.get();
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("sum =" + "时间:" + (end - start));
}
15. 异步回调
Future设计的初衷,对将来的某个事件的结果进行建模。
这里的异步回调类似于前端的Ajax,发起一个异步请求,有成功回调,有失败回调。java中有个CompletableFuture是Future的实现类,用于做异步回调这件事。关于异步回调有两种情况:无返回值的异步回调;有返回值的异步回调。
针对无返回值的异步回调:
- 异步任务发起的时候不会占用程序的执行时间,好比下面代码异步任务需要等待两秒,下面的结果1111会直接执行,不会等待异步任务执行完成再执行。
- 发起一个请求 执行一个异步任务(参数是一个runnable(lambda表达式)) 异步任务发起的时候不会占用程序的执行时间
- 用的是runAsync(),参数是一个runnable参数,function函数式接口。
运行结果:1111
ForkJoinPool.commonPool-worker-1runAsync=>Void
public static void main(String[] args) throws ExecutionException, InterruptedException {
//========================无返回值的异步回调================================//
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
});
System.out.println("1111");
}
针对有返回值的异步回调:
- 有返回值的话就是要么成功,要么失败。成功的话返回的是值,失败的话返回错误信息
- 用的是supplyAsync(),参数是一个供给型函数,供给型函数没有参数,但是有返回值。
- 值的获取用get方法,whenComplete方法获取执行结果,正常的话就返回结果t,失败的话就打印错误信息,可以返回错误码。具体使用如下述程序。
public static void main(String[] args) throws ExecutionException, InterruptedException {
//========================有返回值的异步回调================================//
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
int i = 10/0;
return 1024;
});//这里是一个供给型的函数(没有参数)
//下面可以获取到结果,成功的话t= 1024值就出来了,失败的话就打印u错误信息。返回错误码233
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t); //正常的返回结果
System.out.println("u=>" + u); //u是错误信息
}).exceptionally((e) -> {
e.getMessage();
return 233; //获取到错误的返回结果
}).get());
}