文章目录
1. Tips:什么是Future类型?
Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果,也就是不能进行异步了。
它的接口定义如下:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
cancel方法 用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning 表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning 为true还是false,肯定返回true。
isCancelled 方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
isDone 方法表示任务是否已经完成,若任务完成,则返回true;
get() 方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
get(long timeout, TimeUnit unit) 用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
2. 正常用future的get(long timeout, TimeUnit unit)进行异步调用
public interface MyAsyncService {
void doTaskOne();
void doTaskTwo();
void doTaskThree();
void doAllTask();
Future<String> futureSimple();
}
@Override
@Async
public Future<String> futureSimple() {
System.out.println("开始做future任务");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("future任务完成,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("future任务 over");
}
controller:
@GetMapping("/futureSimple")
@ResponseBody
public Long futureSimple() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(2000);
Future<String> future = myAsyncService.futureSimple();
myAsyncService.doTaskThree();
String result = future.get(2, TimeUnit.SECONDS);
if (result == null) {
result = "null";
}
System.out.println("future任务是否完成:"+future.isDone());
System.out.println(result);
myAsyncService.doTaskOne();
long end = System.currentTimeMillis();
System.out.println("直接返回耗时:" + (end - start));
return (end - start);
}
我们的理想情况是直接返回,然后开启三个任务,然后三个任务执行完但是顺序未知
实际结果:
开始做future任务
开始做任务三
future任务完成,耗时:1873毫秒
future任务是否完成:true
future任务 over
直接返回耗时:3891
开始做任务一
完成任务一,耗时:266毫秒
完成任务三,耗时:3338毫秒
发现满足需求,其实这种情况和不是futrue差不多,但是还有一种情况就是2秒内任务没有执行完,代码就会报异常,因为我们让程序睡眠的时间是随机的,才会出现这个情况,如下:
开始做future任务
开始做任务三
2019-06-10 20:17:26 112335 [http-nio-8080-exec-4] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet]
- Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
- [Request processing failed; nested exception is java.util.concurrent.TimeoutException]
- with root cause
java.util.concurrent.TimeoutException: null
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
....
....
....
future任务完成,耗时:2045毫秒
完成任务三,耗时:3288毫秒
而且发现这种情况,到异常的地方之后的代码就不会执行了,也就是不会再执行任务一
3. 正常用future的get( )阻塞代码进行异步调用
@GetMapping("/futureSimpleTwo")
@ResponseBody
public Long futureSimpleTwo() throws Exception {
long start = System.currentTimeMillis();
Thread.sleep(2000);
Future<String> future = myAsyncService.futureSimple();
myAsyncService.doTaskThree();
String result = future.get();
System.out.println("future任务是否完成:"+future.isDone());
System.out.println(result);
myAsyncService.doTaskOne();
long end = System.currentTimeMillis();
System.out.println("直接返回耗时:" + (end - start));
return (end - start);
}
结果如下:
开始做future任务
开始做任务三
future任务完成,耗时:2574毫秒
future任务是否完成:true
future任务 over
直接返回耗时:4577
开始做任务一
完成任务三,耗时:9457毫秒
完成任务一,耗时:9591毫秒
其实这个情况就是刚才get(long timeout, TimeUnit unit)
里面的指定时间内完成的情况,因为代码会一直阻塞到 那个任务执行完成
4. @Async能给我们带来什么好处?
4.1 改造我们的service
public interface MyAsyncService {
void doTaskOne();
void doTaskTwo();
void doTaskThree();
void doAllTask();
Future<String> futureSimple();
Future<String> futureSimpleTwo();
Future<String> futureSimpleThree();
}
4.2 serviceImpl
package com.pf.org.cms.hcg.system.service.impl;
import com.pf.org.cms.hcg.system.service.MyAsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.Random;
import java.util.concurrent.Future;
@Service
public class MyAsyncImpl implements MyAsyncService {
public static Random random = new Random();
@Async
@Override
public void doTaskOne() {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}
@Override
@Async
public void doTaskTwo() {
System.out.println("开始做任务二");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
}
@Override
@Async
public void doTaskThree() {
System.out.println("开始做任务三");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
}
@Override
@Async
public void doAllTask() {
System.out.println("这个方法会调用三个异步方法");
long start = System.currentTimeMillis();
doTaskOne();
doTaskTwo();
doTaskThree();
long end = System.currentTimeMillis();
System.out.println("完成三个方法,耗时:" + (end - start) + "毫秒");
}
@Override
@Async
public Future<String> futureSimple() {
System.out.println("开始做future任务");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("future任务完成,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("future任务 over");
}
@Override
@Async
public Future<String> futureSimpleTwo() {
System.out.println("开始做futureTwo任务");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("futureTwo任务完成,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("futureTwo任务 over");
}
@Override
@Async
public Future<String> futureSimpleThree() {
System.out.println("开始做futureThree任务");
long start = System.currentTimeMillis();
try {
Thread.sleep(random.nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("futureThree任务完成,耗时:" + (end - start) + "毫秒");
return new AsyncResult<>("futureThree任务 over");
}
}
4.3 改造controller
@GetMapping("/futureSimpleTime")
@ResponseBody
public Long futureSimpleTime() throws Exception {
long start = System.currentTimeMillis();
Future<String> task1 = myAsyncService.futureSimple();
Future<String> task2 = myAsyncService.futureSimpleTwo();
Future<String> task3 = myAsyncService.futureSimpleThree();
while (true) {
if (task1.isDone() && task2.isDone() && task3.isDone()) {
// 三个任务都调用完成,退出循环等待
break;
}
Thread.sleep(1000);
}
long end = System.currentTimeMillis();
System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");
return end - start;
}
4.4 结果分析
开始做future任务
开始做futureTwo任务
开始做futureThree任务
futureTwo任务完成,耗时:3406毫秒
future任务完成,耗时:3548毫秒
futureThree任务完成,耗时:5840毫秒
任务全部完成,总耗时:6008毫秒
可以看到,通过异步调用,让任务一、二、三并发执行,有效的减少了程序的总运行时间。
我们原来每一个任务完成都需要很长时间,加起来要1万多毫秒,但是用了异步之后就花了6千多毫秒,省去了不少的时间,起到了多线程的类似功能