11、java5线程池之异步任务CompletionService

时间:2021-12-05 02:13:59

JDK文档描述:

public interface CompletionService<V>

将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者 submit 执行的任务。使用者 take 已完成的任务,并按照完成这些任务的顺序处理它们的结果。例如,CompletionService 可以用来管理异步 IO ,执行读操作的任务作为程序或系统的一部分提交,然后,当完成读操作时,会在程序的不同部分执行其他操作,执行操作的顺序可能与所请求的顺序不同。 

通常,CompletionService 依赖于一个单独的 Executor 来实际执行任务,在这种情况下,CompletionService 只管理一个内部完成队列。ExecutorCompletionService 类提供了此方法的一个实现。 

内存一致性效果:线程中向 CompletionService 提交任务之前的操作 happen-before 该任务执行的操作,后者依次 happen-before 紧跟在从对应 take() 成功返回的操作
所有已知实现类:
ExecutorCompletionService

子类的JDK描述:

public class ExecutorCompletionService<V>extends Objectimplements CompletionService<V>
使用提供的 Executor 来执行任务的 CompletionService。此类将安排那些完成时提交的任务,把它们放置在可使用 take 访问的队列上。该类非常轻便,适合于在执行几组任务时临时使用。

所有的方法:

方法摘要
 Future<V> poll()
          获取并移除表示下一个已完成任务的 Future,如果不存在这样的任务,则返回 null
 Future<V> poll(long timeout,
TimeUnit unit)


          获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则将等待指定的时间(如果有必要)。
 Future<V> submit(Callable<V> task)

          提交要执行的值返回任务,并返回表示挂起的任务结果的 Future。
 Future<V> submit(Runnable task, V result)

          提交要执行的 Runnable 任务,并返回一个表示任务完成的 Future,可以提取或轮询此任务。
 Future<V> take()

          获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。

JDK自带的例子:

假定您有针对某个问题的一组求解程序,每个求解程序都能返回某种类型的 Result 值,并且您想同时运行它们,使用方法 use(Result r) 处理返回非 null 值的每个求解程序的返回结果。可以这样编写程序

void solve(Executor e, Collection<Callable<Result>> solvers)
throws InterruptedException, ExecutionException {
CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
for (Callable<Result> s : solvers)
ecs.submit(s);
int n = solvers.size();
for (int i = 0; i < n; ++i) {
Result r = ecs.take().get();
if (r != null)
use(r);
}
}

假定您想使用任务集中的第一个非 null 结果,而忽略任何遇到异常的任务,并且在第一个任务就绪时取消其他所有任务:

void solve(Executor e, Collection<Callable<Result>> solvers)
throws InterruptedException {
CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
int n = solvers.size();
List<Future<Result>> futures = new ArrayList<Future<Result>>(n);
Result result = null;
try {
for (Callable<Result> s : solvers)
futures.add(ecs.submit(s));
for (int i = 0; i < n; ++i) {
try {
Result r = ecs.take().get();
if (r != null) {
result = r;
break;
}
} catch(ExecutionException ignore) {}
}
}
finally {
for (Future<Result> f : futures)
f.cancel(true);
} if (result != null)
use(result);
}

自己写的例子:(谁先得到结果我就先取到谁的结果)

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class ThreadPool2 {
public static void main(String[] args) {
test2();
} private static void test2(){
ExecutorService es = Executors.newFixedThreadPool(5);
CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(es);
for(int i=1 ; i<=5; i++){
final int task = i;
cs.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Callable 任务【" + task + "】运行完成");
return new Random().nextInt(100);
}
});
}
System.out.println("我是取结果前的代码");
//异步取结果
for(int i=0; i<5; i++){
try {
System.out.println(cs.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("我是取结果后的代码");
es.shutdown();
}
}

运行的结果:

我是取结果前的代码
Callable 任务【3】运行完成
25
Callable 任务【1】运行完成
16
Callable 任务【4】运行完成
38
Callable 任务【5】运行完成
17
Callable 任务【2】运行完成
4
我是取结果后的代码