线程池:CompletionService
接口CompletionService的功能是以异步的方式一边生产新的任务,一边处理已完成任务的结果,这样可以将执行任务与处理任务分离开来进行处理。
使用submit执行任务,使用take取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果
接口CompletionService完全可以避免FutureTask类阻塞的缺点,可更加有效地处理Future的返回值,也就是哪个任务先执行完,CompletionService就先取得这个任务的返回值再处理。
使用CompletionService解决Future的缺点
使用CompletionService接口中的take()方法,
它的主要作用就是取得Future对象,方法声明结构如下:public Future<V> take() throws InterruptedException
package ExecutorCompletionService_0;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private String userName;
private Long sleepValue;
public MyCallable(String userName,Long sleepValue){
super();
this.userName=userName;
this.sleepValue=sleepValue;
}
@Override
public String call() throws Exception {
System.out.println("**"+userName);
Thread.sleep(sleepValue);
return "KK "+userName;
}
}
package ExecutorCompletionService_0;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class Run {
public static void main(String[] args) {
try {
//接口Future具有阻塞同步性,这样的代码运行效率会大打折扣,接口CompletionService可以解决这样的问题
MyCallable callable=new MyCallable("1",5000L);
MyCallable callable1=new MyCallable("2",4000L);
MyCallable callable2=new MyCallable("3",3000L);
MyCallable callable3=new MyCallable("4",2000L);
MyCallable callable4=new MyCallable("5",1000L);
List<Callable> callables=new ArrayList<Callable>();
callables.add(callable);
callables.add(callable1);
callables.add(callable2);
callables.add(callable3);
callables.add(callable4);
ThreadPoolExecutor executor=new ThreadPoolExecutor(5,10,5, TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>());
CompletionService service=new ExecutorCompletionService(executor);
for (int i=0;i<5;i++){
service.submit(callables.get(i));
}
for (int i=0;i<5;i++){
String str= (String) service.take().get();
System.out.println(str);
// 从打印的结果来看,CompletionService完全解决了Future阻塞的特性,
// 也就是使用CompletionService接口后,哪个任务先执行完,哪个任务的返回值就先打印。
// 在CompletionService接口中如果当前没有任务被执行完,则csRef. take().get()方法还是呈阻塞特性。
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
使用take()方法
方法take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。
package ExecutorCompletionService_1;
import java.util.concurrent.*;
public class Run {
public static void main(String[] args) {
try {
//方法take()取得最先完成任务的Future对象,谁执行时间最短谁最先返回。
ExecutorService service=Executors.newCachedThreadPool();
CompletionService completionService=new ExecutorCompletionService(service);
for (int i=0;i<10;i++){
service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep((long) Math.random()*1000);
return Thread.currentThread().getName();
}
});
}
for (int i=0;i<10;i++){
System.out.println(completionService.take().get());
//从运行结果来看,方法take()是按任务执行的速度,
//从快到慢的顺序获得Future对象,因为打印的时间是从小到大。
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
使用poll()方法
方法poll()的作用是获取并移除表示下一个已完成任务的Future,如果不存在这样的任务,则返回null,方法poll()无阻塞的效果。
package ExecutorCompletionService_1;
import java.util.concurrent.*;
public class Run {
public static void main(String[] args) {
try {
//方法poll()的作用是获取并移除表示下一个已完成任务的Future,
// 如果不存在这样的任务,则返回null,方法poll()无阻塞的效果。
ExecutorService service=Executors.newCachedThreadPool();
CompletionService completionService=new ExecutorCompletionService(service);
for (int i=0;i<10;i++){
service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep((long) Math.random()*1000);
return Thread.currentThread().getName();
}
});
}
for (int i=0;i<10;i++){
System.out.println(completionService.poll()!=null);
//方法poll()返回的Future为null,因为当前没有任何已完成任务的Future对象,所以返回为null,
// 通过此实验证明poll()方法不像take()方法具有阻塞的效果。
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用poll(long timeout, TimeUnit unit)方法
方法Future<V> poll(long timeout, TimeUnit unit)的作用是等待指定的timeout时间,在timeout时间之内获取到值时立即向下继续执行,如果超时也立即向下执行。
方法poll()中timeout的值如果小于任务执行的时间,则返回值就是null。
类CompletionService与异常
使用CompletionService执行任务的过程中不可避免会出现各种情况的异常,下面来实验一下CompletionService对异常处理的流程。
虽然MyCallableB.java出现异常,但并没有调用FutureTask类的get()方法,所以不出现异常。
调用get方法后就会捕获到callable中抛出的异常


浙公网安备 33010602011771号