SpingBoot异步执行

异步任务常用场景

  • 发送消息
  • 发送邮件
  • App消息推送
  • 节省运维凌晨发布任务时间提供效率

示例:
springboot启动类加上@EnableAsync注解,异步线程任务开关
异步任务:

@Component
public class TestAsyncTask {

    @Async
    public Future<String> doTask1() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("任务 1 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }

    @Async
    public Future<String> doTask2() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(800);
        long end = System.currentTimeMillis();
        System.out.println("任务 2 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }

    @Async
    public Future<String> doTask3() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(600);
        long end = System.currentTimeMillis();
        System.out.println("任务 3 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }
}

执行异步任务

@RestController
@RequestMapping("task")
public class DoTask {

    @Autowired
    private TestAsyncTask asyncTask;

    @RequestMapping("testAsync")
    public String execAsyncTask() throws Exception {

        long start = System.currentTimeMillis();

        Future<String> a = asyncTask.doTask1();
        Future<String> b = asyncTask.doTask2();
        Future<String> c = asyncTask.doTask3();

        while (true) {
            if (a.isDone() && b.isDone() && c.isDone()) {
                break;
            }
        }

        long end = System.currentTimeMillis();

        String times = "任务全部完成,总耗时:" + (end - start) + " ms";
        System.out.println(times);
        System.out.println(a);
        return times;
    }
}

输出结果
是大多数
异步执行用时少于同步执行

异步方法与同步方法执行过程
TestService

@Service
public class TestService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async
    public void asyncMethod() {
        sleep();
        logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
    }

    public void syncMethod() {
        sleep();
    }

    private void sleep() {
        try {
            logger.info("睡眠开始");
            TimeUnit.SECONDS.sleep(2);
            logger.info("睡眠结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

TestController

@RestController
public class TestController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private TestService testService;

    @GetMapping("async")
    public void testAsync() {
        long start = System.currentTimeMillis();
        logger.info("异步方法开始");

        testService.asyncMethod();

        logger.info("异步方法结束");
        long end = System.currentTimeMillis();
        logger.info("总耗时:{} ms", end - start);
    }

    @GetMapping("sync")
    public void testSync() {
        long start = System.currentTimeMillis();
        logger.info("同步方法开始");

        testService.syncMethod();

        logger.info("同步方法结束");
        long end = System.currentTimeMillis();
        logger.info("总耗时:{} ms", end - start);
    }
}

访问http://localhost:8080/async同步执行结果:

15:29:44.559  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 同步方法开始
15:29:44.577  INFO 5476 --- [nio-8080-exec-1] com.example.demo.service.TestService     : 睡眠开始
15:29:46.579  INFO 5476 --- [nio-8080-exec-1] com.example.demo.service.TestService     : 睡眠结束
15:29:46.580  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 同步方法结束
15:29:46.580  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 总耗时:2021 ms

访问http://localhost:8080/sync,异步执行结果

15:29:58.169  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 异步方法开始
15:29:58.186  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 异步方法结束
15:29:58.187  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 总耗时:18 ms
15:29:58.195  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 睡眠开始
15:30:00.196  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 睡眠结束
15:30:00.196  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 异步方法内部线程名称:task-1

弊端:每次执行异步任务都需要新建一个线程。每次线程的创建与销毁会造成不必要的开销。可使用自定义线程池

添加线程池配置类

@Configuration
public class AsyncPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数,默认为1
        executor.setCorePoolSize(20);
        //线程池最大线程数。只有当核心线程都被用完并且缓冲队列满后,才会开始申超过请核心线程数的线程,默认值为Integer.MAX_VALUE
        executor.setMaxPoolSize(200);
        //缓冲队列
        executor.setQueueCapacity(25);
        //超出核心线程数的线程的空闲时候的最大存活时间
        executor.setKeepAliveSeconds(200);
        //线程名字前缀
        executor.setThreadNamePrefix("asyncThread");
        //是否等待全部线程执行完毕才关闭线程池。默认false
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //setWaitForTasksToCompleteOnShutdown的等待时长,默认为0,不等待
        executor.setAwaitTerminationSeconds(60);
        //线程的处理策略 默认为abortPolicy
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        executor.initialize();
        return executor;
    }
}

修改TestService
@async加上线程池名称
@Async("asyncThreadPoolTaskExecutor")
再次运行http://localhost:8080/async ,结果:

15:45:56.975  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 异步方法开始
15:45:56.987  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 异步方法结束
15:45:56.988  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 总耗时:12 ms
15:45:57.003  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 睡眠开始
15:45:59.005  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 睡眠结束
15:45:59.005  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 异步方法内部线程名称:asyncThread1

异步回调

若异步方法有返回值,可使用Future接收回调值

@Async("asyncThreadPoolTaskExecutor")
public Future<String> asyncMethod() {
    sleep();
    logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
    return new AsyncResult<>("hello async");
}

AsyncResult为Spring实现的Future实现类
在这里插入图片描述

@GetMapping("async")
public String testAsync() throws Exception {
    long start = System.currentTimeMillis();
    logger.info("异步方法开始");

    Future<String> stringFuture = testService.asyncMethod();
    String result = stringFuture.get();
    logger.info("异步方法返回值:{}", result);
    
    logger.info("异步方法结束");

    long end = System.currentTimeMillis();
    logger.info("总耗时:{} ms", end - start);
    return stringFuture.get();
}

通过future的get方法获取异步调用的返回值

在这里插入图片描述
通过返回结果我们可以看出Future的get方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行。get还有一个get(long timeout, TimeUnit unit)重载方法,我们可以通过这个重载方法设置超时时间,即异步方法在设定时间内没有返回值的话,直接抛出java.util.concurrent.TimeoutException异常。
比如设置超时时间为60秒:
String result = stringFuture.get(60, TimeUnit.SECONDS);

发布了43 篇原创文章 · 获赞 3 · 访问量 2007
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览