SpringBoot中的异步调用@Async

如何开启异步调用

在SpringBoot中,只需要给方法加上@Async注解,就能将同步方法变为异步调用。

首先在启动类上添加@EnableAsync,即开启异步调用。

/**
 * @author qcy
 */ @SpringBootApplication @EnableAsync public class AsyncApplication { public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }

}

在需要异步调用的方法上加上@Async注解

package com.yang.async; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; /**
 * @author qcy
 * @create 2020/09/09 14:01:35
 */ @Slf4j @Component public class Task { @Async public void method1() {
        log.info("method1开始,执行线程为" + Thread.currentThread().getName()); try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("method1结束");
    } @Async public void method2() {
        log.info("method2开始,执行线程为" + Thread.currentThread().getName()); try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("method2结束");
    }


}

测试一下:

@SpringBootTest @Slf4j public class AsyncApplicationTests { @Autowired Task task; @Test public void testAsyncWithVoidReturn() throws InterruptedException {
        log.info("main线程开始");

        task.method1();
        task.method2(); //确保两个异步调用执行完成 Thread.sleep(6000);

        log.info("main线程结束");
    }

}

输出如下:

可以看得出,SpringBoot创建了一个名为applicationTaskExecutor的线程池,使用这里面的线程来执行异步调用。

这里值得注意的是,不要在一个类中调用@Async标注的方法,否则不会起到异步调用的作用,至于为什么会产生这样的问题,需要深入到源码中一探究竟,会另开篇幅。

既然默认使用的是SpringBoot自己创建的applicationTaskExecutor,那如何自己去定义一个线程池呢?


自定义线程池

我们需要手动创建一个名为asynTaskExecutord的Bean

package com.yang.async; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; /**
 * @author qcy
 * @create 2020/09/09 15:31:07
 */ @Slf4j @Configuration public class AsyncConfig { @Bean public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(16);
        executor.setQueueCapacity(50);
        executor.setAllowCoreThreadTimeOut(true);
        executor.setKeepAliveSeconds(10);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setThreadNamePrefix("async-thread-pool-thread"); return executor;
    }
}

对以上参数不了解的同学,可以参考我的这篇文章说说线程池

其他类不需要变动,直接运行刚才的testAsyncWithVoidReturn()方法,输出:

看得出来,现在是我们自定义的线程池

如果关心异步调用的返回值,又怎么处理?


获取异步调用的返回结果

获取异步调用的结果,需要利用Future机制,可以参考我的另外一篇文章谈谈Runnable、Future、Callable、FutureTask之间的关系

为Task类增加以下两个方法:

@Async public Future<String> method3() {
        log.info("method3开始,执行线程为" + Thread.currentThread().getName()); try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("method3结束"); return new AsyncResult<>("method3");
    } @Async public Future<String> method4() {
        log.info("method4开始,执行线程为" + Thread.currentThread().getName()); try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("method4结束"); return new AsyncResult<>("method4");
    }

测试类:

@Test public void testAsyncWithStringReturn() throws InterruptedException, ExecutionException {
        log.info("main线程开始");

        Future<String> method3Result = task.method3();
        Future<String> method4Result = task.method4(); //get方法为阻塞获取 log.info("method3执行的返回结果:{}", method3Result.get());
        log.info("method4执行的返回结果:{}", method4Result.get());
        log.info("main线程结束");
    }

输出:

如图,在主线程结束前,获取到了异步调用的结果。且在两个异步调用都结束的情况下,继续执行主线程。

全部评论
更多内容请移步我的博客https://blog.csdn.net/qq_33591903
点赞 回复 分享
发布于 2020-09-11 10:42

相关推荐

码农索隆:这种hr,建议全中国推广
点赞 评论 收藏
分享
白火同学:只是实习的话,你这份简历应该也差不多了。真要优化的话,因为面实习的话,没有开发经验,面试更重视技术栈水平。 1、重视JavaSE的基础吧,集合、泛型算是比较基础的基础,多线程、反射、JVM内存模型才是基础; 2、技术栈写到具体的点,比如Elasticsearch的使用写到某个点,限制面试官自由发挥,防止问了相关问题最后又答不上,如果真没把握建议不写,降低面试官的心理预期; 3、技术栈不要重复,比如技术栈第二条和第八条可以合并改为“熟悉Redis中间件,包括基本数据结构、缓存策略、持久化机制,了解缓存三剑客及其解决方案,并有相关项目经验。”; 4、项目指标量化,比如“达到xx秒的响应速度”(不过这个就有点偏校招社招的要求了,实习简历不写也无伤大雅)。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务