Java Concurrency - invokeAny & invokeAll

时间:2022-09-25 17:29:53

Running multiple tasks and processing the first result

A common problem in concurrent programming is when you have various concurrent tasks that solve a problem, and you are only interested in the first result of those tasks. For example, you want to sort an array. You have various sort algorithms. You can launch all of them and get the result of the first one that sorts these, that is, the fastest sorting algorithm for a given array.

In this case, you are going to implement an example where a user can be validated by two mechanisms. The user will be validated if one of those mechanisms validates it.

/**
* This class implement a simulation of a user validation system. It suspend the Thread
* a random period of time and then returns a random boolean value. We consider that it
* returns the true value when the user is validated and the false value when it's not
*/
public class UserValidator { /**
* The name of the validation system
*/
private String name; public UserValidator(String name) {
this.name=name;
} /**
* Method that validates a user
* @param name Name of the user
* @param password Password of the user
* @return true if the user is validated and false if not
*/
public boolean validate(String name, String password) {
// Create a new Random objects generator
Random random=new Random(); // Sleep the thread during a random period of time
try {
Long duration=(long)(Math.random()*5);
System.out.printf("Validator %s: Validating a user during %d seconds\n",this.name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
return false;
} // Return a random boolean value
return random.nextBoolean();
} /**
* Return the name of the validation system
* @return The name of the validation system
*/
public String getName(){
return name;
} } /**
* This class encapsulate a user validation system to be executed as a Callable object.
* If the user is validated, it returns the name of the validation system. If not,
* it throws an Exception
*/
public class TaskValidator implements Callable<String> { /**
* The user validator used to validate the user.
*/
private UserValidator validator;
/**
* The name of the user
*/
private String user;
/**
* The password of the user
*/
private String password; /**
* Constructor of the class
* @param validator The user validator system used to validate it
* @param user The name of the user
* @param password The password of the user
*/
public TaskValidator(UserValidator validator, String user, String password){
this.validator=validator;
this.user=user;
this.password=password;
} /**
* Core method of the Callable interface. Tries to validate the user using the user
* validation system. If the user is validated, returns the name of the validation system.
* If not, throws and Exception
* @return The name of the user validation system.
* @throws Exception An exception when the user is not validated
*/
@Override
public String call() throws Exception {
if (!validator.validate(user, 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 found\n",validator.getName());
return validator.getName();
} } /**
* This is the main class of the example. Creates two user validation systems and execute
* them in an Executor using the invokeAny() method. If the user is validated by one of the
* user validation systems, then it shows a message. If both system don't validate the user,
* the application processes the ExecutionException thrown by the method
*/
public class Main { public static void main(String[] args) { // Initialize the parameters of the user
String username = "test";
String password = "test"; // Create two user validation objects
UserValidator ldapValidator = new UserValidator("LDAP");
UserValidator dbValidator = new UserValidator("DataBase"); // Create two tasks for the user validation objects
TaskValidator ldapTask = new TaskValidator(ldapValidator, username, password);
TaskValidator dbTask = new TaskValidator(dbValidator, username, password); // Add the two tasks to a list of tasks
List<TaskValidator> taskList = new ArrayList<>();
taskList.add(ldapTask);
taskList.add(dbTask); // Create a new Executor
ExecutorService executor = (ExecutorService) Executors.newCachedThreadPool();
String result;
try {
// Send the list of tasks to the executor and waits for the result of the first task
// that finish without throw any Exception. If all the tasks throw an Exception, the
// method throws an ExecutionException.
result = executor.invokeAny(taskList);
System.out.printf("Main: Result: %s\n",result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} // Shutdown the Executor
executor.shutdown();
System.out.printf("Main: End of the Execution\n");
} }

The key of the example is in the Main class. The invokeAny() method of the ThreadPoolExecutor class receives a list of tasks, launches them, and returns the result of the first task that finishes without throwing an exception. This method returns the same data type that the call() method of the tasks you launch returns. In this case, it returns a String value.

The following screenshot shows the output of an execution of the example when one task validates the user:

Java Concurrency - invokeAny & invokeAll

The example has two UserValidator objects that return a random boolean value. Each UserValidator object is used by a Callable object, implemented by the TaskValidator class. If the validate() method of the UserValidator class returns a false value, the TaskValidator class throws Exception. Otherwise, it returns the true value.

So, we have two tasks that can return the true value or throw an Exception exception. You can have the following four possibilities:

  • Both tasks return the true value. The result of the invokeAny() method is the name of the task that finishes in the first place.
  • The first task returns the true value and the second one throws Exception. The result of the invokeAny() method is the name of the first task.
  • The first task throws Exception and the second one returns the true value. The result of the invokeAny() method is the name of the second task.
  • Both tasks throw Exception. In that class, the invokeAny() method throws an ExecutionException exception.

If you run the examples several times, you get the four possible solutions you can get.

The following screenshot shows the output of the application when both tasks throw an exception:

Java Concurrency - invokeAny & invokeAll

The ThreadPoolExecutor class provides another version of the invokeAny() method:

invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit): This method executes all the tasks and returns the result of the first one that finishes without throwing an exception, if it finishes before the given timeout passes. The TimeUnit class is an enumeration with the following constants: DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS.

Running multiple tasks and processing all the results

The Executor framework allows you to execute concurrent tasks without worrying about thread creation and execution. It provides you the Future class that you can use to control the status and get the results of any task executed in an executor.

When you want to wait for the finalization of a task, you can use the following two methods:

  • The isDone() method of the Future interface returns true if the task has finished its execution.
  • The awaitTermination() method of the ThreadPoolExecutor class puts the thread to sleep until all the tasks have finished their execution after a call to the shutdown() method.

These two methods have some drawbacks. With the first one, you can only control the completion of a task, and with the second one, you have to shutdown the executor to wait for a thread, otherwise the method's call returns immediately.

The ThreadPoolExecutor class provides a method that allows you to send to the executor a list of tasks and wait for the finalization of all the tasks in the list.

In this case, you will learn how to use this feature by implementing an example with three tasks executed and their results printed out when they finish.

/**
* This class stores the result generated by one task
*
*/
public class Result { private int num;
private long factorial; public Result(int num, long factorial) {
super();
this.num = num;
this.factorial = factorial;
} public int getNum() {
return num;
} public long getFactorial() {
return factorial;
}
} /**
* This class calculates the factorial of a number.
*/
public class FactorialCalculator implements Callable<Result> { /**
* Number to calculate the factorial
*/
private int num; /**
* Constructor of the class. Initializes the attributes
* @param num Number to calculate the factorial
*/
public FactorialCalculator(int num) {
this.num = num;
} /**
* Method called by the executor to execute this task and calculate the factorial of a number
*/
@Override
public Result call() throws Exception {
TimeUnit.SECONDS.sleep(new Random().nextInt(3));
if (num < 0) {
throw new IllegalArgumentException("The number must be less than zero.");
}
if (num <= 1) {
return new Result(num, 1L);
}
long f = 1;
for (int i = 2; i <= num; i++) {
f *= num;
}
return new Result(num, f);
} } /**
* Main class of the example. Launch three tasks using the invokeAll() method
* and then prints their results to the console
*/
public class Main { public static void main(String[] args) { // Create three tasks and stores them in a List
List<Callable<Result>> taskList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Callable<Result> factorialTask = new FactorialCalculator(new Random().nextInt(10));
taskList.add(factorialTask);
} // Create an executor
ExecutorService executor = (ExecutorService) Executors.newCachedThreadPool(); // Call the invokeAll() method
List<Future<Result>> resultList = new ArrayList<>();
try {
resultList = executor.invokeAll(taskList);
} catch (InterruptedException e) {
e.printStackTrace();
} // Finish the executor
executor.shutdown(); // Writes the results to the console
for (int i = 0; i < resultList.size(); i++) {
try {
Result result = resultList.get(i).get();
System.out.printf("%d! = %d\n", result.getNum(), result.getFactorial());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
} }

In this case, you have learned how to send a list of tasks to an executor and wait for the finalization of all of them using the invokeAll() method. This method receives a list of the Callable objects and returns a list of the Future objects. This list will have a Future object per task in the list. The first object in the list of the Future objects will be the object that controls the first task in the list of the Callable objects, and so on.

The first point to take into consideration is that the type of data used for the parameterization of the Future interface in the declaration of the list that stores the result objects must be compatible with the one used to parameterized the Callable objects. In this case, you have used the same type of data: the Result class.

Another important point about the invokeAll() method is that you will use the Future objects only to get the results of the tasks. As the method finishes when all the tasks have finished, if you call the isDone() method of the Future objects that is returned, all the calls will return the true value.

The ExecutorService class provides another version of the invokeAll() method:

  • invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit): This method executes all the tasks and returns the result of their execution when all of them have finished, if they finish before the given timeout passes. The TimeUnit class is an enumeration with the following constants: DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS.

Java Concurrency - invokeAny & invokeAll的更多相关文章

  1. Java Concurrency in Practice 读书笔记 第十章

    粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...

  2. Java Concurrency - 浅析 CountDownLatch 的用法

    The Java concurrency API provides a class that allows one or more threads to wait until a set of ope ...

  3. Java Concurrency - 浅析 CyclicBarrier 的用法

    The Java concurrency API provides a synchronizing utility that allows the synchronization of two or ...

  4. Java Concurrency - 浅析 Phaser 的用法

    One of the most complex and powerful functionalities offered by the Java concurrency API is the abil ...

  5. Java Concurrency - 线程执行器

    Usually, when you develop a simple, concurrent-programming application in Java, you create some Runn ...

  6. Java Concurrency - Callable &amp&semi; Future

    One of the advantages of the Executor framework is that you can run concurrent tasks that return a r ...

  7. 深入浅出 Java Concurrency &lpar;4&rpar;&colon; 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

  8. 《Java Concurrency》读书笔记,使用JDK并发包构建程序

    1. java.util.concurrent概述 JDK5.0以后的版本都引入了高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线并发编程的,充分利用了现代多处 ...

  9. 深入浅出 Java Concurrency &lpar;15&rpar;&colon; 锁机制 part 10 锁的一些其它问题

      主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的 ...

随机推荐

  1. mac pods 安装

    CocoaPods的安装步骤: 1.先配置Ruby环境: 若已安装Ruby,请升级Ruby环境 sudo gem update --system 若没有安装,则请看下面, 以下代码区域,带有 $ 打头 ...

  2. &lbrack;mysql&rsqb;MySQL忘记密码

    1.修改MySQL的登录设置: vi /etc/my.cnf 在[mysqld]的段中加上一句:skip-grant-tables 保存并且退出vi. 2.重新启动mysqld # /etc/init ...

  3. BZOJ 1202 狡猾的商人

    前缀和+带权并查集. #include<iostream> #include<cstdio> #include<cstring> #include<algor ...

  4. 利用KVC使用自定义的控件

    KVC简单使用: 可以用来设置属性的值例如有个Person类下有个属性name [self setvalue:@"yourname" forkey:@"name&quot ...

  5. Cesium原理篇:glTF

    关键字:Cesium glTF WebGL技术 大纲: 1 glTF简介,这是一个什么东西,有哪些特点 2 Cesium如何加载,渲染glTF,逻辑结构和关键技术 3 个人总结,从glTF学习如何设计 ...

  6. windows phone 8&period;1 开发&colon;后台任务详解

    原文出自:http://www.bcmeng.com/backtask/ 小梦今天给大家分享一下windows phone 8.1中的后台任务如何实现,许多应用都会用到后台任务,所以我们必须得掌握. ...

  7. 【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-1 蒙特卡罗 &lpar;一&rpar;

    今天起,我们就开始学习第三本书了 这本书主要讲的是蒙特卡罗渲染,以及相关的数学.术语概念等 这本书相较于前面两本有着什么不同,承担着什么样的任务,尚涉书未深,姑妄言之: 第一本书,带领我们初探光线追踪 ...

  8. 关于页面传值页面的跳转,以及spring mvc 框架的流程问题

    list页面 1.点击页面后,进入后台的list方法中,通过findPage()查询数据的,findPage中含有findList(): 2.如果页面没有输入查询条件,那么则显示所有数据集合,如果页面 ...

  9. 代码处理 iOS 的横竖屏旋转

    一.监听屏幕旋转方向 在处理iOS横竖屏时,经常会和UIDeviceOrientation.UIInterfaceOrientation和UIInterfaceOrientationMask这三个枚举 ...

  10. Spring源码解析-事件

    Spring事件的组件 主要是3个组件: 1.ApplicationEvent   事件 2.ApplicationListener 监听器,对事件进行监听 3.ApplicationEventMul ...