执行者分离任务的启动和结果的处理
通常,当你使用执行者执行并发任务时,你将会提交 Runnable或Callable任务给这个执行者,并获取Future对象控制这个方法。你可以发现这种情况,你需要提交任务给执行者在一个对象中,而处理结果在另一个对象中。基于这种情况,Java并发API提供CompletionService类。
CompletionService 类有一个方法来提交任务给执行者和另一个方法来获取已完成执行的下个任务的Future对象。在内部实现中,它使用Executor对象执行任务。这种行为的优点是共享一个CompletionService对象,并提交任务给执行者,这样其他(对象)可以处理结果。其局限性是,第二个对象只能获取那些已经完成它们的执行的任务的Future对象,所以,这些Future对象只能获取任务的结果。
在这个指南中,你将学习如何使用CompletionService类把执行者启动任务和处理它们的结果分开。
代码
package com.packtpub.java7.concurrency.chapter4.recipe11.core;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.packtpub.java7.concurrency.chapter4.recipe11.task.ReportProcessor;
import com.packtpub.java7.concurrency.chapter4.recipe11.task.ReportRequest;
/**
* Main class of the example creates all the necessary objects and throws the tasks
*
*/
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
// Create the executor and thee CompletionService using that executor
ExecutorService executor=(ExecutorService)Executors.newCachedThreadPool();
CompletionService<String> service=new ExecutorCompletionService<>(executor);
// Crete two ReportRequest objects and two Threads to execute them
ReportRequest faceRequest=new ReportRequest("Face", service);
ReportRequest onlineRequest=new ReportRequest("Online", service);
Thread faceThread=new Thread(faceRequest);
Thread onlineThread=new Thread(onlineRequest);
// Create a ReportSender object and a Thread to execute it
ReportProcessor processor=new ReportProcessor(service);
Thread senderThread=new Thread(processor);
// Start the Threads
System.out.printf("Main: Starting the Threads\n");
faceThread.start();
onlineThread.start();
senderThread.start();
// Wait for the end of the ReportGenerator tasks
try {
System.out.printf("Main: Waiting for the report generators.\n");
faceThread.join();
onlineThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Shutdown the executor
System.out.printf("Main: Shuting down the executor.\n");
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// End the execution of the ReportSender
processor.setEnd(true);
System.out.printf("Main: Ends\n");
}
}
package com.packtpub.java7.concurrency.chapter4.recipe11.task;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* This class simulates the generation of a report. Is a Callable
* object that will be executed by the executor inside a
* CompletionService
*
*/
public class ReportGenerator implements Callable<String> {
/**
* The sender of the report
*/
private String sender;
/**
* The title of the report
*/
private String title;
/**
* Constructor of the class. Initializes the two attributes
* @param sender The sender of the report
* @param title The title of the report
*/
public ReportGenerator(String sender, String title){
this.sender=sender;
this.title=title;
}
/**
* Main method of the ReportGenerator. Waits a random period of time
* and then generates the report as a String.
*/
@Override
public String call() throws Exception {
try {
Long duration=(long)(Math.random()*10);
System.out.printf("%s_%s: ReportGenerator: Generating a report during %d seconds\n",this.sender,this.title,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
String ret=sender+": "+title;
return ret;
}
}
package com.packtpub.java7.concurrency.chapter4.recipe11.task;
import java.util.concurrent.CompletionService;
/**
* This class represents every actor that can request a report. For this example,
* it simply create three ReportGenerator objects and execute them through a
* CompletionService
*
*/
public class ReportRequest implements Runnable {
/**
* Name of this ReportRequest
*/
private String name;
/**
* CompletionService used for the execution of the ReportGenerator tasks
*/
private CompletionService<String> service;
/**
* Constructor of the class. Initializes the parameters
* @param name Name of the ReportRequest
* @param service Service used for the execution of tasks
*/
public ReportRequest(String name, CompletionService<String> service){
this.name=name;
this.service=service;
}
/**
* Main method of the class. Create three ReportGenerator tasks and executes them
* through a CompletionService
*/
@Override
public void run() {
ReportGenerator reportGenerator=new ReportGenerator(name, "Report");
service.submit(reportGenerator);
}
}
package com.packtpub.java7.concurrency.chapter4.recipe11.task;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* This class will take the results of the ReportGenerator tasks executed through
* a CompletinoService
*
*/
public class ReportProcessor implements Runnable {
/**
* CompletionService that executes the ReportGenerator tasks
*/
private CompletionService<String> service;
/**
* Variable to store the status of the Object. It will executes until the variable
* takes the true value
*/
private boolean end;
/**
* Constructor of the class. It initializes the attributes of the class
* @param service The CompletionService used to execute the ReportGenerator tasks
*/
public ReportProcessor (CompletionService<String> service){
this.service=service;
end=false;
}
/**
* Main method of the class. While the variable end is false, it
* calls the poll method of the CompletionService and waits 20 seconds
* for the end of a ReportGenerator task
*/
@Override
public void run() {
while (!end){
try {
Future<String> result=service.poll(20, TimeUnit.SECONDS);
if (result!=null) {
String report=result.get();
System.out.printf("ReportReceiver: Report Recived: %s\n",report);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.printf("ReportSender: End\n");
}
/**
* Method that establish the value of the end attribute
* @param end New value of the end attribute.
*/
public void setEnd(boolean end) {
this.end = end;
}
}
结果
Main: Starting the Threads
Main: Waiting for the report generators.
Main: Shuting down the executor.
Online_Report: ReportGenerator: Generating a report during 7 seconds
Face_Report: ReportGenerator: Generating a report during 2 seconds
ReportReceiver: Report Recived: Face: Report
ReportReceiver: Report Recived: Online: Report
ReportSender: End
Main: Ends
工作原理
在示例的主类中,你使用Executors类的newCachedThreadPool()方法创建ThreadPoolExecutor。然后,使用这个对象初始化一个CompletionService对象,因为CompletionService需要使用一个执行者来执行任务。利用CompletionService执行一个任务,你需要使用submit()方法,如在ReportRequest类中。
当其中一个任务被执行,CompletionService完成这个任务的执行时,这个CompletionService在一个队列中存储Future对象来控制它的执行。poll()方法用来查看这个列队,如果有任何任务执行完成,那么返回列队的第一个元素,它是一个已完成任务的Future对象。当poll()方法返回一个Future对象时,它将这个Future对象从队列中删除。这种情况下,你可以传两个属性给那个方法,表明你想要等任务结果的时间,以防队列中的已完成任务的结果是空的。
一旦CompletionService对象被创建,你创建2个ReportRequest对象,用来执行3个ReportGenerator任务,每个都在CompletionService中,和一个ReportSender任务,它将会处理已提交给2个ReportRequest对象的任务所产生的结果。
扩展
CompletionService类可以执行Callable和Runnable任务。在这个示例中,你已经使用Callable,但你同样可以提交Runnable对象。由于Runnable对象不会产生结果,CompletionService类的理念不适用于这些情况。
这个类同样提供其他两个方法,用来获取已完成任务的Future对象。这两个方法如下:
poll():不带参数版本的poll()方法,检查是否有任何Future对象在队列中。如果列队是空的,它立即返回null。否则,它返回第一个元素,并从列队中删除它。
take():这个方法,不带参数。检查是否有任何Future对象在队列中。如果队列是空的,它阻塞线程直到队列有一个元素。当队列有元素,它返回第一元素,并从列队中删除它。
转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 线程执行者(十一)执行者分离任务的启动和结果的处理