目录
CompletionService
当希望使用一个对象来提交任务给执行器,并想在另外一个对象中处理返回结果时,可以使用CompletionService的实现ExecutorCompletionService类。
- submit(),用于提交任务
- poll(),检查队列中是否有Future对象,如果没有就返回null,否则返回并删除队列中的第一个。
- take(),检查队列中是否有Future对象,如果没有就会阻塞当前线程直到队列不为空,否则返回并删除队列中的第一个。
案例説明
模拟两个请求,这2个请求将2个任务提交给执行器,由另外的处理线程对执行器的结果进行消费。
一、主程序
package xyz.jangle.thread.test.n4_X.completionservice;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 4.10 CompletionService,在执行器内分离任务的启动并处理返回结果
* 模拟两个请求,这2个请求将2个任务提交给执行器,由另外的处理线程对执行器的结果进行消费。
*
* @author jangle
* @email jangle@jangle.xyz
* @time 2020年8月25日 下午6:00:16
*
*/
public class M {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorCompletionService<String> service = new ExecutorCompletionService<String>(executor);
// 创建2个请求
ReportRequest aRequest = new ReportRequest("A", service);
ReportRequest bRequest = new ReportRequest("B", service);
Thread aThread = new Thread(aRequest);
Thread bThread = new Thread(bRequest);
// 创建1个处理报告结果的线程
ReportProcessor processor = new ReportProcessor(service);
Thread senderThread = new Thread(processor);
System.out.println("Main:开始执行");
aThread.start();
bThread.start();
senderThread.start();
try {
System.out.println("Main:等待生产报告的请求执行完毕(提交给执行器)");
aThread.join();
bThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main:报告请求提交完毕,关闭执行器并等待执行器内的任务执行完");
// important 此处是关闭执行器,而不是service
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 任务执行完毕后,让processor对结果进行处理。
processor.stopProcessing();
System.out.println("Main:执行完毕");
}
}
二、生产报告的类(任务)
由线程执行器执行的任务
package xyz.jangle.thread.test.n4_X.completionservice;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* 模拟生产报告的类
* @author jangle
* @email jangle@jangle.xyz
* @time 2020年8月25日 下午6:00:49
*
*/
public class ReportGenerator implements Callable<String> {
private final String sender;
private final String title;
public ReportGenerator(String sender, String title) {
super();
this.sender = sender;
this.title = title;
}
@Override
public String call() throws Exception {
try {
long duration = (long) (Math.random() * 10);
System.out.println(this.sender + "_" + this.title + ":等待" + duration + "秒(模拟生产报告)");
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回报告结果
String ret = sender + ":" + title;
return ret;
}
}
三、请求
将任务提交给线程执行器。
package xyz.jangle.thread.test.n4_X.completionservice;
import java.util.concurrent.CompletionService;
/**
* 模拟生成报告的请求
* 该线程的任务是将生产动作提交给执行器进行生产执行。
* @author jangle
* @email jangle@jangle.xyz
* @time 2020年8月25日 下午6:07:54
*
*/
public class ReportRequest implements Runnable {
private final String name;
private final CompletionService<String> service;
public ReportRequest(String name, CompletionService<String> service) {
this.name = name;
this.service = service;
}
@Override
public void run() {
ReportGenerator reportGenerator = new ReportGenerator(name, "ReportTitle");
// reportGenerator作为任务提交给执行器ExecutorCompletionService。
// 任务完成之后ExecutorCompletionService会将结果添加到它的Queue中。其他线程调用它的poll方法可以获取到。
// 详见源码
service.submit(reportGenerator);
}
}
四、消费线程(处理结果的线程)
对线程执行器的结果进行处理。
package xyz.jangle.thread.test.n4_X.completionservice;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* 专门来处理报告的生产结果(类似与消费线程)
*
* @author jangle
* @email jangle@jangle.xyz
* @time 2020年8月25日 下午6:16:12
*
*/
public class ReportProcessor implements Runnable {
private final CompletionService<String> service;
// 结束任务的开关
private volatile boolean end;
public ReportProcessor(CompletionService<String> service) {
this.service = service;
this.end = false;
}
@Override
public void run() {
System.out.println("End:" + end);
while (!end) {
try {
// 获取结果队列的第一个元素,如果没有,则等待2秒,若等待2秒还没有,则返回null
Future<String> future = this.service.poll(2, TimeUnit.SECONDS);
if (future != null) {
String report = future.get();
System.out.println("ReportProcessor:report的结果:" + report);
} else {
System.out.println("future:" + future);
}
} catch (InterruptedException | ExecutionException e) {
// ExecutionException 任务执行时,因为异常而结束的,对其进行获取结果操作时,会抛出ExecutionException
e.printStackTrace();
}
}
System.out.println("发送者:End");
}
public void stopProcessing() {
System.out.println("结束process的运行");
this.end = true;
}
}
五、执行结果
Main:开始执行
Main:等待生产报告的请求执行完毕(提交给执行器)
End:false
Main:报告请求提交完毕,关闭执行器并等待执行器内的任务执行完
A_ReportTitle:等待9秒(模拟生产报告)
B_ReportTitle:等待2秒(模拟生产报告)
future:null
ReportProcessor:report的结果:B:ReportTitle
future:null
future:null
future:null
ReportProcessor:report的结果:A:ReportTitle
结束process的运行
Main:执行完毕
future:null
发送者:End

本文介绍如何使用 Java 的 CompletionService 接口分离任务提交与结果处理,通过 ExecutorCompletionService 类实现多线程环境下任务的异步执行与结果消费,提供了一个包含主程序、任务类、请求类及结果处理线程的完整示例。
762

被折叠的 条评论
为什么被折叠?



