目录
主要区别:
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-4和Thread-5,这是新开的线程,不是线程池中的线程了。
总结:
Future与CompletableFuture使用场景不一样,都支持get方法,如果异步执行完后需要同步,使用Future,反之,如果异步执行完后,不需要等待,直接异步操作,那么使用CompletableFuture。