方法描述:
/**
* Executes the given tasks, returning the result
* of one that has completed successfully (i.e., without throwing
* an exception), if any do. Upon normal or exceptional return,
* tasks that have not completed are cancelled.
* The results of this method are undefined if the given
* collection is modified while this operation is in progress.
*
* @param tasks the collection of tasks
* @param <T> the type of the values returned from the tasks
* @return the result returned by one of the tasks
* @throws InterruptedException if interrupted while waiting
* @throws NullPointerException if tasks or any element task
* subject to execution is {@code null}
* @throws IllegalArgumentException if tasks is empty
* @throws ExecutionException if no task successfully completes
* @throws RejectedExecutionException if tasks cannot be scheduled
* for execution
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
方法说明:使用invokeAny()方法,当任意一个线程找到结果之后,立刻终结【中断】所有线程。
执行结果可能会有以下三种情况:
- 任务都执行成功,使用过第一个任务返回的结果。
- 任务都失败了,抛出Exception,invokeAny方法将抛出ExecutionException。
- 部分任务失败了,会使用第一个成功的任务返回的结果。
玩具代码:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//使用invokeAny()方法,当任意一个线程找到结果之后,立刻终结【中断】所有线程
public class UserValidator {
private String name;
public UserValidator(String name){
this.name = name;
}
//Mock验证过程,有可能会抛出异常
public boolean validate(String name ,String password){
Random random = new Random();
try{
long duration = (long)(Math.random()*10);
System.out.printf("Validator %s: Validating a user during %d seconds \n", this.name,duration);
TimeUnit.SECONDS.sleep(duration);
}catch(InterruptedException e){
e.printStackTrace();
return false;
}
return random.nextBoolean();
}
public String getName(){
return name;
}
public static class TaskValidator implements Callable<String>{
private UserValidator validator;
private String name;
private String password;
public TaskValidator(UserValidator validator,String name,String password){
this.validator = validator;
this.name = name;
this.password = password;
}
@Override
public String call() throws Exception {
if(!validator.validate(name, password)){
System.out.printf("%s : The user has not been found \n", validator.getName());
throw new Exception("Error validating user");
}
System.out.printf("%s: The user has been fount \n", validator.getName());
return validator.getName();
}
}
public static void main(String[] args) {
String username = "test";
String password = "test";
UserValidator ldapValidator = new UserValidator("LDAP");
UserValidator dbValidator = new UserValidator("DataBase");
TaskValidator ldapTask = new TaskValidator(ldapValidator,username,password);
TaskValidator dbTask = new TaskValidator(dbValidator,username,password);
List<TaskValidator> taskList = new ArrayList<>();
taskList.add(ldapTask);
taskList.add(dbTask);
ExecutorService executor = (ExecutorService)Executors.newCachedThreadPool();
String result ;
try{
//只执行成功其中任何一个即可,会中断其他线程
result = executor.invokeAny(taskList);
System.out.printf("Main: Result : %s \n", result);
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}
executor.shutdown();
System.out.printf("Main: End of the Execution \n");
}
}
AbstractExecutorService.invokeAny()实现逻辑:
/**
* the main mechanics of invokeAny.
*/
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
// For efficiency, especially in executors with limited
// parallelism, check to see if previously submitted tasks are
// done before submitting more of them. This interleaving
// plus the exception mechanics account for messiness of main
// loop.
try {
// Record exceptions so that if we fail to obtain any
// result, we can throw the last exception we got.
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
// Start one task for sure; the rest incrementally
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}