1. 在线文档
  2. 项目结构
1.源码克隆:git clone https://github.com/spring-guides/gs-async-method.git
2.包含两个项目initial和complete,initial可以根据文档练习完善,complete是完整项目
3.功能描述:构建查找服务,异步查询 GitHub 用户信息并通过 GitHub 的 API 检索数据

springboot 异步错误使用导致cpu负载过高_java

  1. 源码分析
1.POM依赖
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>
2.入口类
// 
@SpringBootApplication
// 
@EnableAsync
public class AsyncMethodApplication {

	public static void main(String[] args) {
		// close the application context to shut down the custom ExecutorService
		SpringApplication.run(AsyncMethodApplication.class, args).close();
	}

	/**
	 * 这段代码定义了一个 Executor 对象,该对象是使用 ThreadPoolTaskExecutor 类创建的线程池。Executor 是一个接口,它定义了一种方法来异步地执行任务。
	 *
	 * ThreadPoolTaskExecutor 是一个实现了 Executor 接口的类,它使用线程池来执行任务。线程池是一种管理线程的机制,它提供了一种可伸缩的线程池,用于在应用程序中执行异步任务。
	 *
	 * 在这段代码中,我们设置了线程池的核心线程数(corePoolSize)和最大线程数(maxPoolSize)为 2,以及队列的容量(queueCapacity)为 500。我们还为线程池中的线程设置了名称前缀(threadNamePrefix)。最后,我们调用 initialize() 方法来初始化线程池。
	 *
	 * 这段代码还使用了 @Bean 注解,该注解表示此方法将会在应用程序上下文中生成一个 bean。这意味着,你可以在应用程序的其他地方使用这个 Executor 对象。
	 *
	 * 例如,你可以在另一个类中注入这个 Executor 对象并使用它来异步地执行任务:
	 * @Autowired
	 * private Executor taskExecutor;
	 *
	 * public void doSomething() {
	 *   taskExecutor.execute(new Runnable() {
	 *     @Override
	 *     public void run() {
	 *       // do something
	 *     }
	 *   });
	 * }
	 * @return
	 */
	@Bean
	public Executor taskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(2);
		executor.setMaxPoolSize(2);
		executor.setQueueCapacity(500);
		executor.setThreadNamePrefix("GithubLookup-");
		executor.initialize();
		return executor;
	}
	
}
3.接口类
@Service
public class GitHubLookupService {

	private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);

	private final RestTemplate restTemplate;

	public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
		this.restTemplate = restTemplateBuilder.build();
	}

	/**
	 * 这段代码定义了一个名为 findUser() 的方法,它使用 RestTemplate 发送一个 GET 请求到 GitHub API,获取指定用户的信息并返回。方法的返回值是 CompletableFuture 类型,它可以用于执行异步操作。
	 *
	 * @Async 注解告诉 Spring 在单独的线程中执行 findUser() 方法。这意味着 findUser() 方法的调用将立即返回,而不等待方法实际完成。你可以在其他代码中继续执行其他任务,而无需等待 findUser() 方法的响应返回。
	 *
	 * findUser() 方法返回的 CompletableFuture 对象可以用于获取方法执行的结果。例如,你可以使用 thenApply() 方法在方法执行完成后对结果进行处理:
	 * CompletableFuture<User> future = findUser("some-user");
	 * future.thenApply(user -> {
	 *     // Do something with the result
	 *     return user;
	 * });
	 * @param user
	 * @return
	 * @throws InterruptedException
	 */
	@Async
	public CompletableFuture<User> findUser(String user) throws InterruptedException {
		logger.info("Looking up " + user);
		String url = String.format("https://api.github.com/users/%s", user);
		User results = restTemplate.getForObject(url, User.class);
		// Artificial delay of 1s for demonstration purposes
		Thread.sleep(1000L);
		return CompletableFuture.completedFuture(results);
	}

}
4.测试类
/**
 * 这段代码定义了一个名为 AppRunner 的组件,它实现了 CommandLineRunner 接口,因此在 Spring 应用程序启动时会执行 run() 方法。
 *
 * AppRunner 类持有一个名为 gitHubLookupService 的 GitHubLookupService 类型的引用,并在构造函数中将其注入。GitHubLookupService 类是一个用于查找 GitHub 用户信息的服务。
 *
 * 在 run() 方法中,代码首先记录了当前时间,然后使用 gitHubLookupService 发出三个异步请求来查找三个 GitHub 用户。请求返回的是 CompletableFuture 类型的对象,这些对象会被存储在 page1、page2 和 page3 中。
 *
 * 接下来,代码调用 allOf() 方法,该方法接受多个 CompletableFuture 对象并返回一个新的 CompletableFuture,该 CompletableFuture 将在所有传入的 CompletableFuture 对象都完成后完成。allOf() 方法的返回值被加入,这意味着程序将在所有请求完成后再继续执行。
 *
 * 最后,代码打印了每个请求的结果以及整个操作所用的时间。get() 方法用于获取 CompletableFuture 对象的结果。
 */
@Component
// 
public class AppRunner implements CommandLineRunner {

	private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);

	private final GitHubLookupService gitHubLookupService;

	public AppRunner(GitHubLookupService gitHubLookupService) {
		this.gitHubLookupService = gitHubLookupService;
	}

	@Override
	public void run(String... args) throws Exception {
		// Start the clock
		long start = System.currentTimeMillis();

		// Kick of multiple, asynchronous lookups
		CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
		CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry");
		CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects");

		// Wait until they are all done
		CompletableFuture.allOf(page1,page2,page3).join();

		// Print results, including elapsed time
		logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
		logger.info("--> " + page1.get());
		logger.info("--> " + page2.get());
		logger.info("--> " + page3.get());

	}

}
  1. 项目演示
前两个调用发生在不同的线程 ( GithubLookup-2, GithubLookup-1) 中,第三个调用被暂停,直到两个线程之一可用为止。
要比较在没有异步功能的情况下这需要多长时间,请尝试注释掉@Async注释并再次运行该服务。总耗用时间应该会显着增加,
因为每个查询至少需要一秒钟。