Spring Boot异步Async使用Future与CompletableFuture的区别

目录

主要区别:

代码示例

1.Future测试:

2.CompletableFuture测试:

总结:


主要区别:

Future:在执行结束后没法回调,调用get方法会被阻塞;

CompletableFuture:在执行结束后可通过whenComplete或whenCompleteAsync方法回调,不会阻塞线程,同时也是支持get方法的;


代码示例

spring boot配置Async,@EnableAsync启动异步

AsyncConfig

package com.test.config;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@EnableAsync
@Configuration
public class AsyncConfig {

	/**
	 * 异步任务自定义线程池
	 */
	@Bean(name="taskExecutor")
	public Executor taskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(50);
		executor.setMaxPoolSize(500);
		executor.setQueueCapacity(300);
		executor.setKeepAliveSeconds(60);
		executor.setThreadNamePrefix("自定义线程-");
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.setWaitForTasksToCompleteOnShutdown(true);
		executor.setAwaitTerminationSeconds(60);
		return executor;
	}
}

1.Future测试:

主线程等待各个异步执行的线程返回的结果来做下一步操作,则必须阻塞在future.get()的地方等待结果返回,这时候又变成同步了。适用于需要等异步结果的场景。

FutureService

package com.test.service;

import java.util.concurrent.Future;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

@Service
public class FutureService {

	@Async
	public Future<String> futureTest1(){
		System.out.println(Thread.currentThread().getName()+"进行任务futureTest1...");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"任务futureTest1完成");
		return new AsyncResult<String>("这是任务futureTest1返回结果");
	}
	
	@Async
	public Future<String> futureTest2(){
		System.out.println(Thread.currentThread().getName()+"进行任务futureTest2...");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"任务futureTest2完成");
		return new AsyncResult<String>("这是任务futureTest2返回结果");
	}
	
}

FutureController

package com.test.controller;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.service.FutureService;

@RestController
@RequestMapping("/future")
public class FutureController {

    @Autowired
    private FutureService futureService;
    //超时时间
  	public static final long timeout = 30;
  	
    @RequestMapping(value = "futureTest", method = RequestMethod.GET)
    public String futureTest() {
    	// 开始时间戳
    	long beginTime = System.currentTimeMillis();
    	
    	Future<String> result1 = futureService.futureTest1();
    	Future<String> result2 = futureService.futureTest2();
		
		//添加结果集,30秒超时
    	Map<String, Object> map = new HashMap<String, Object>();
    	try {
    		String str1 = result1.get(timeout, TimeUnit.SECONDS);
    		System.out.println(str1);
    		
    		String str2 = result2.get(timeout, TimeUnit.SECONDS);
    		System.out.println(str2);
    		
    		map.put("result1", str1);
    		map.put("result2", str2);
    		//这里需要等get()完成后才会执行,因为get()方法会阻塞
            System.out.println("map集合: "+map.size());
    		System.out.println("回调后的任务: "+Thread.currentThread().getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
    	System.out.println("耗时: "+(System.currentTimeMillis() - beginTime));
    	return "success";
    }
    
}

 打印结果

自定义线程-1进行任务futureTest1...
自定义线程-2进行任务futureTest2...
自定义线程-1任务futureTest1完成
这是任务futureTest1返回结果
自定义线程-2任务futureTest2完成
这是任务futureTest2返回结果
map集合: 2
回调后的任务: http-nio-8082-exec-1
耗时: 5068

大家可以看到,这时候map集合里面是有值的,主线程http-nio-8082-exec-1是在异步执行完才执行的,因为get方法是会阻塞线程的。耗时5秒是以异步中耗时最长的方法为准,因为要等耗时最长的方法执行完,才能合并。


2.CompletableFuture测试:

实现了Future和CompletionStage接口,保留了Future的优点,并且弥补了其不足。即异步的任务完成后,需要用其结果继续操作时,无需等待。适用于不需要等异步结果的场景。

CompletableFutureService

package com.test.service;

import java.util.concurrent.CompletableFuture;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class CompletableFutureService {
	
	@Async
	public CompletableFuture<String> completableFuture1(){
		System.out.println(Thread.currentThread().getName()+"进行任务completableFuture1...");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"任务completableFuture1完成");
		return CompletableFuture.completedFuture("这是任务completableFuture1返回结果");
	}
	
	@Async
	public CompletableFuture<String> completableFuture2(){
		System.out.println(Thread.currentThread().getName()+"进行任务completableFuture2...");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"任务completableFuture2完成");
		return CompletableFuture.completedFuture("这是任务completableFuture2返回结果");
	}
	
}

CompletableFutureController

package com.test.controller;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.service.CompletableFutureService;

@RestController
@RequestMapping("/completable")
public class CompletableFutureController {

    @Autowired
    private CompletableFutureService completableFutureService;
    
    @RequestMapping(value = "completableFutureTest", method = RequestMethod.GET)
    public String CompletableFutureTest() {
    	// 开始时间戳
    	long beginTime = System.currentTimeMillis();
    	
    	CompletableFuture<String> result1 = completableFutureService.completableFuture1();
    	CompletableFuture<String> result2 = completableFutureService.completableFuture2();
		
		//添加结果集,30秒超时
    	Map<String, Object> map = new HashMap<String, Object>();
    	try {
    		result1.whenComplete((r, t)->{
    			System.out.println(r+Thread.currentThread().getName());
    			map.put("result1", r);
    		});
    		result2.whenComplete((r, t)->{
    			System.out.println(r+Thread.currentThread().getName());
    			map.put("result2", r);
    		});
    		
    		//这里不用等前面的结果集,会异步先执行
    		System.out.println("map集合: "+map.size());
    		System.out.println("回调后的任务: "+Thread.currentThread().getName());
		} catch (Exception e) {
			e.printStackTrace();
		}
    	System.out.println("耗时: "+(System.currentTimeMillis() - beginTime));
    	return "success";
    }
    
}

打印结果

map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果自定义线程-1
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果自定义线程-2

大家可以看到,这时候map集合里面是空的,主线程http-nio-8082-exec-1是在异步之前打印的,说明使用whenComplete是异步的,不会阻塞线程的。耗时33毫秒不用等异步执行完就能打印。

这里简单说下whenComplete和whenCompleteAsync的区别:

whenComplete:执行完当前任务的线程,继续执行 whenComplete 的任务。
whenCompleteAsync: 执行完当前任务的线程,把whenCompleteAsync 的任务继续提交给线程池来执行。(可能开启新的线程)

 把前面的改成whenCompleteAsync测试一下

result1.whenCompleteAsync((r, t)->{
    			System.out.println(r+Thread.currentThread().getName());
    			map.put("result1", r);
    		});
    		result2.whenCompleteAsync((r, t)->{
    			System.out.println(r+Thread.currentThread().getName());
    			map.put("result2", r);
    		});

打印结果

map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果Thread-4
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果Thread-5

区别的地方在于Thread-4Thread-5,这是新开的线程,不是线程池中的线程了。


总结:

Future与CompletableFuture使用场景不一样,都支持get方法,如果异步执行完后需要同步,使用Future,反之,如果异步执行完后,不需要等待,直接异步操作,那么使用CompletableFuture。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涛哥是个大帅比

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值