sprngboot使用@Async实现异步调用二:使用Future以及定义超时

地址:
https://github.com/HouChenggong/springboot_Async/blob/master/src/main/java/com/pf/org/cms/hcg/system/service/MyAsyncService.java

传送门:service

传送门:serviceImpl

传送门:controller

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千多毫秒,省去了不少的时间,起到了多线程的类似功能

传送门:service

传送门:serviceImpl

传送门:controller

猜你喜欢

转载自blog.csdn.net/qq_39455116/article/details/91397795