前言:
1、实现功能大体与"Callable 和 FutureTask 的使用" 相似
2、此文主要用于介绍CompletionService实现的利弊,故请先了解FutureTask 相关知识
下面比较FutureTask 和 CompletionService 的差别:
代码如下:
public class MyCompletionService implements Callable<String> {
private String userName;
private long sleepTime;
public MyCompletionService(String userName, long sleepTime) {
this.userName = userName;
this.sleepTime = sleepTime;
}
@Override
public String call() throws Exception {
Thread.sleep(sleepTime);
return userName;
}
public static void main(String[] args) throws InterruptedException,ExecutionException {
MyCompletionService service1 = new MyCompletionService("aaa",1800);
MyCompletionService service2 = new MyCompletionService("bbb",1000);
MyCompletionService service3 = new MyCompletionService("ccc",3000);
MyCompletionService service4 = new MyCompletionService("ddd",2400);
MyCompletionService service5 = new MyCompletionService("eee",3600);
List<Callable> list = new ArrayList<Callable>();
list.add(service1);
list.add(service2);
list.add(service3);
list.add(service4);
list.add(service5);
ExecutorService executor = Executors.newFixedThreadPool(10);
// CompletionService
CompletionService<String> completionService = new ExecutorCompletionService(executor);
System.out.println("startTime:"+System.currentTimeMillis());
for (int i = 0;i<5;i++){
// 提交对象必须是callable类型
completionService.submit(list.get(i));
}
for (int i = 0;i<5;i++){
System.out.println("等待打印第"+(i+1)+"个返回值");
System.out.println("userName:"+completionService.take().get()+"-当前时间:"+System.currentTimeMillis());
}
System.out.println("endTime:"+System.currentTimeMillis());
// futureTask
FutureTask<String> task1 = new FutureTask<>(service1);
FutureTask<String> task2 = new FutureTask<>(service2);
FutureTask<String> task3 = new FutureTask<>(service3);
FutureTask<String> task4 = new FutureTask<>(service4);
FutureTask<String> task5 = new FutureTask<>(service5);
List<FutureTask> ftList = new ArrayList<>();
ftList.add(task1);
ftList.add(task2);
ftList.add(task3);
ftList.add(task4);
ftList.add(task5);
System.out.println("startTime:"+System.currentTimeMillis());
for (int i = 0;i < 5;i++){
executor.execute(ftList.get(i));
}
for (int i = 0;i < 5;i++) {
System.out.println("等待打印第"+(i+1)+"个返回值");
System.out.println("userName:"+ftList.get(i).get()+"---"+System.currentTimeMillis());
}
System.out.println("endTime:"+System.currentTimeMillis());
}
}
上方的为CompletionService执行结果,下方为FutrueTask执行结果
其实从业务执行时间上看,消耗时间实质上差不多,但区别是:
1,FutureTask一定是按照调用get()方法的顺序执行的,但调用get()方法是会阻塞主线程,如果两个子线程第一个执行速度慢于第二个线程,那么输出的一定是同时输出。
2,CompletionService的则是按照线程先执行完的顺序输出的,改善了FutureTask的get()方法阻塞!。
下面执行一个业务系统的demo,其他类的代码在"Callable 和 FutureTask 的使用"一文中。
public Map<String,Object> sendMailAndMessage2(Map<String, Object> params){
MailCallable mailCallable = new MailCallable(params);
MessageCallable messageCallable = new MessageCallable(params);
List<Callable> list = new ArrayList<>();
list.add(mailCallable);
list.add(messageCallable);
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 将线程池作为参数传入completionService中
CompletionService completionService = new ExecutorCompletionService(executor);
Map<String,Object> map = new HashMap<String, Object>();
try {
for (int i = 0; i < list.size(); i++) {
completionService.submit(list.get(i));
}
for (int i = 0; i < list.size(); i++) {
map.putAll((Map<String, Object>)completionService.take().get());
}
}catch (Exception e){
e.printStackTrace();
}
return map;
}
执行结果:
必要的五步:
//1、创建线程池(这里需要考虑线程池是单独给以下逻辑使用,还是全局使用的问题,单独使用放在方法中,全局使用放在外部)
ThreadPoolExecutor executor = DefaultThreadPoolFactory.initThreadPool(5, 10);
//2、创建异步执行器(VO只是一个实体类,没有做任何处理)
CompletionService<VO> completionService = new ExecutorCompletionService(executor);
//3、任务提交给异步执行器(这里submit的类对象必须实现Callable接口,此处用匿名内部类的方式new CallAble(){}})
completionService.submit(() -> {
//业务代码
});
//4、获取执行结果(submit了几个,获取几次)
for (int i = 0; i < VOList.size(); i++) {
VO vo = completionService.take().get();
if (null != vo){
dataList.add(vo);
}
}
//5、关闭线程池
executor.shutdown();
实际中应用例子(浙江省xxx项目中源代码)
// 创建线程池,有自己已经封装好的代码类
ThreadPoolExecutor executor = DefaultThreadPoolFactory.initThreadPool(5, 10);
// 创建异步执行器,泛型里写要操作的实体类
CompletionService<VO> completionService = new ExecutorCompletionService(executor);
// list中的vo都并发执行操作
for (VO vo : baseVOList) {
completionService.submit(() -> {
//业务代码
});
}
// 获取每个线程执行的值
for (int i = 0; i < baseVOList.size(); i++) {
SentimentProcessBaseVO vo = completionService.take().get();
if (null != vo){
dataList.add(vo);
}
}
// 关闭线程池
executor.shutdown();