10.在执行器中分离任务的启动与结果的处理
当我们要在一个对象中发送任务给执行器,在另一个对象中处理结果时,对于这种需求可以使用CompletionService类。
该类有一个方法用来发送任务给执行器,还有一个方法为下一个已经执行结束的任务获取Future对象。从内部实现机制来看,CompletionService类使用Executor对象来执行任务。
这个行为
的优势是可以共享CompletionService对象,并发送任务到执行器,然后其他的对象可以处理任务的结果。第二个方法有不足之处,只能为已经执行结束的任务获取Future对象,因此,
这些Future对象只能被用来获取任务的结果。
该类可以执行Callable或者Runnable类型的任务。由于使用Runnable接口对象不能产生结果,因此CompletionService的基本原则不适用。
CompletionService类提供了其他两种方法来获取任务已经完成的Future对象。
1.poll():无参数的poll()方法用于检查队列中是否有Future对象。如果队列为空,则立即返回null,否则将返回队列中的第一个元素,并移除该元素。
2.take(): 这个方法检查队列中是否有Future对象。如果队列为空,则会阻塞线程直到队列中有可用的元素。如果队列中有元素,它将返回队列中的第一个元素,并移除该元素。
/**
*
* @author fcs
* @date 2015-5-8
* 描述:在执行器中分离任务的启动与结果的处理
* 说明:使用需求:需要在一个对象里发送任务给执行器,然后在另一个对象里处理结果
*/
public class ReportGenerator implements Callable<String>{
private String sender;
private String title;
public ReportGenerator(String sender, String title) {
this.sender = sender;
this.title = title;
}
@Override
public String call() throws Exception {
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);
String ret = sender+":"+title;
return ret;
}
}
/**
*
* @author fcs
* @date 2015-6-16
* 描述:该类将获取到ReportGenerator任务的结果
*/
public class ReportProcessor implements Runnable{
private CompletionService<String> service;
private boolean end;
public ReportProcessor(CompletionService<String> service) {
this.service = service;
end = false;
}
@Override
public void run() {
while(!end){
try {
//调用CompletionService的poll()方法来获取下一个已经完成任务的Future对象
Future<String> result = service.poll(20, TimeUnit.SECONDS);
if(result != null){
String report;
//使用该方法获取任务的结果。
report = result.get();
System.out.printf("ReportReceiver: Report Received: %s\n",report);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ReportSender: End\n");
}
}
public void setEnd(boolean end){
this.end = end;
}
}
/**
*
* @author fcs
* @date 2015-5-9
* 描述:模拟请求报告
*/
public class ReportRequest implements Runnable {
private String name;
private CompletionService<String> service;
public ReportRequest(String name, CompletionService<String> service) {
this.name = name;
this.service = service;
}
/**
* 调用service的submi()方法将ReportGenerator对象发送给CompletionService对象
*/
@Override
public void run() {
ReportGenerator reportGenerator = new ReportGenerator(name, "Report");
service.submit(reportGenerator);
}
}
public class Main {
public static void main(String[] args) {
ExecutorService executorService = (ExecutorService)Executors.newCachedThreadPool();
CompletionService<String> service = new ExecutorCompletionService<String>(executorService);
//创建两个ReportRequest对象,然后创建两个线程Thread对象分别执行他们
ReportRequest faceRequest = new ReportRequest("Face", service);
ReportRequest onlineRequest = new ReportRequest("Online",service);
Thread faceThread = new Thread(faceRequest);
Thread onlineThread = new Thread(onlineRequest);
ReportProcessor processor = new ReportProcessor(service);
Thread senderThread = new Thread(processor);
System.out.println("Main: Starting the Threads\n");
faceThread.start();
onlineThread.start();
senderThread.start();
System.out.println("Main: Waiting for the report generators.\n");
//等待ReportRequest线程的结束
try {
faceThread.join();
onlineThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Shutting down the executor.\n");
executorService.shutdown();
try {
//调用该方法等待所有的任务执行结束
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
processor.setEnd(true);
System.out.println("Main: Ends");
}
}
11.处理在执行器中被拒绝的任务
如果在shutdown()方法与执行器结束之间发送一个任务给执行器,这个任务会被拒绝,因为这个时间段执行器不再接收任务了,
ThreadPoolExecutor类提供了一套机制,当任务被拒绝时调用
这套机制来处理它们。
使用RejectedExecutionHandler接口。
为了处理在执行器中被拒绝的任务,需要创建一个实现RejectedExecutionHandler接口的处理类。这个接口有一个rejectedExecution方法,其中有两个参数:
1.一个Runnable对象,用来存储被拒绝的任务,。
2.一个Executor对象,用来存储任务被拒绝的执行器。
被执行器拒绝的每一个任务都将调用该方法,需要先调用Executor类的setRejectedExecutionHandler()方法来设置用于被拒绝的任务的处理程序。
当执行器接收一个任务并开始执行时,先检查shutdown()方法是否已经被调用了,如果是那么执行器就会拒绝这个任务。首先,执行器会寻找通过setRejectedExecutionHandler()方法设置的
用于被拒绝的任务的处理程序,如果找到一个处理程序,执行器就会调用其rejectedExecution()方法,否则就会抛出RejectedExecutionException异常,这是一个运行时异常,
因此并不需要catch语句来对其进行处理。
/**
*
* @author fcs
* @date 2015-5-9
* 描述:处理在执行器中被拒绝的任务
* 说明:当我们想结束执行器的执行时,调用shutdown()方法来表示执行器应当结束,但是,执行器只有等待
* 正在运行的任务或者等待执行的任务结束后,才能真正结束。
* 如果在shutdown()方法与执行器结束之间发送一个任务给执行器,这个任务会被拒绝,
* 因为这个时间段执行器已经不再接受任务了,ThreadPoolExecutor类提供了一套机制,
* 当任务呗拒绝时调用这套机制来处理它们
* 本节使用RejectedExecutionHandler接口,该接口只有一个方法
* rejectedExecution方法
*
*/
public class RejectedTaskController implements RejectedExecutionHandler{
@Override //在控制台输出已被拒绝的任务的名称和执行器的状态
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.printf("RejectedTaskController: The task %s has been rejected \n",r.toString());
System.out.printf("RejectedTaskController: %s\n",executor.toString());
System.out.printf("RejectedTaskController: Terminating: %s\n",executor.isTerminating());
System.out.printf("RejectedTaskController: Terminated: %s\n",executor.isTerminated());
}
}
public class Task implements Runnable{
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Task "+name+ ": starting");
long duration = (long)(Math.random() *10);
System.out.printf("Task %s: ReportGenerator: Generatint a report during %d seconds\n",name,duration);
try {
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Task %s: Ending \n",name);
}
@Override //重写toString方法,返回任务的名称
public String toString() {
return name;
}
}
public class Main {
public static void main(String[] args) {
RejectedTaskController controller = new RejectedTaskController();
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();
//设置用于被拒绝的任务的处理程序
executor.setRejectedExecutionHandler(controller);
System.out.printf("Main: Starting.\n");
for(int i =0;i< 3;i++){
Task task = new Task("Task_"+i);
executor.submit(task);
}
System.out.println("Main:Shutting down the executor.\n");
executor.shutdown();
System.out.println("Main: Sending another Task\n");
//关闭执行器后再发送一个任务给执行器
Task task = new Task("RejectedTask");
executor.submit(task);
System.out.println("Main: End.");
}
}