运行多任务并处理首个结果

Java 9并发编程指南 目录

当使用多种有效的并发任务解决问题时,会产生一个普遍的问题,那就是只需要第一个结果。例如,想要排列数组,可以使用各种各样的排列算法。也就是说,对指定的数组用最快的排序算法进行排序,然后从排好序的数组中找到第一个结果。

本节中,学习使用ThreadPoolExecutor类来实现这个场景。使用两种机制来尝试并确认一个用户。当其中一种机制能够确认的话,此用户则被认证通过。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤完成范例:

  1. 创建名为UserValidator的类,实现用户认证过程:

    public class UserValidator {
    
  2. 定义名为name的私有String属性,用来存储用户认证系统的用户名:

    	private final String name;
    
  3. 实现类构造函数,初始化属性:

    	public UserValidator(String name) {
    		this.name = name;
    	}
    
  4. 实现validate()方法,接收两个String参数,分别是需要认证的用户名和用户密码:

    	public boolean validate(String name , String password) {
    
  5. 创建名为random的Random对象:

    		Random random = new Random();
    
  6. 随机等待一段时间来模拟用户认证过程:

    		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) {
    			return false;
    		}
    
  7. 返回随机的Boolean值,当用户认证通过时,validate()方法返回true,否则false:

    		return random.nextBoolean();
    	}
    
  8. 实现getName()方法,返回名字的属性值:

    	public String getName() {
    		return name;
    	}
    
  9. 现在,创建名为ValidatorTask的类,使用UserValidation对象作为并发任务,来执行认证过程。指定其实现String类参数化的Callable接口:

    public class ValidatorTask implements Callable<String>{
    
  10. 定义名为validator的私有UserValidator属性:

    	private final UserValidator validator;
    
  11. 定义两个私有String属性,分别是user和password:

    	private final String user;
    	private final String password;
    
  12. 实现类构造函数,初始化这些属性:

    	public ValidatorTask(UserValidator validator, String user, String password) {
    		this.validator = validator;
    		this.user = user;
    		this.password = password;
    	}
    
  13. 实现call()方法,返回String对象:

    	@Override
    	public String call() throws Exception {
    
  14. 如果用户未通过UserValidator对象认证,输出对应信息到控制台并且抛出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");
    		}
    
  15. 否则,输出信息到控制台知名用户已认证通过,并返回UserUserValidator对象的名称:

    		System.out.printf("%s : The user has been found\n", validator.getName());
    		return validator.getName();
    	}
    
  16. 现在实现范例的主方法,创建一个包含main()方法的Main类:

    public class Main {
    	public static void main(String[] args) {
    
  17. 创建两个名为user和password的String对象,初始化测试值:

    		String username = "test";
    		String password = "test";
    
  18. 创建两个UserValidator对象,名为ldapValidator和dbValidator:

    		UserValidator ldapValidator = new UserValidator("LDAP");
    		UserValidator dbValidator = new UserValidator("DataBase");
    
  19. 创建两个名为ldapTask和dbTask的TaskValidator对象,分别用ldapValidator和dbValidator初始化:

    		ValidatorTask ldapTask = new ValidatorTask(ldapValidator, username, password);
    		ValidatorTask dbTask = new ValidatorTask(dbValidator, username, password);
    
  20. 创建TaskValidator对象列表,将已创建的两个对象添加进去:

    		List<ValidatorTask> taskList = new ArrayList<>();
    		taskList.add(ldapTask);
    		taskList.add(dbTask);
    
  21. 使用Executors类的newCachedThreadPool()方法创建新的ThreadPoolExecutor对象,以及名为result的字符串变量:

    		ExecutorService executor = (ExecutorService)Executors.newCachedThreadPool();
    		String result;
    
  22. 调用执行器对象的invokeAny()方法,这个方法将taskList 作为参数接收且返回String。同样的,将返回的String对象输出到控制台:

    		try {
    			result = executor.invokeAny(taskList);
    			System.out.printf("Main : Result : %s\n", result);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}
    
  23. 使用shutdown()方法终止执行器,输出程序已结束的信息到控制台:

    		executor.shutdown();
    		System.out.printf("Main : End of the Execution\n");
    

工作原理

本范例的关键点在Main类中,ThreadPoolExecutor类的invokeAny()方法接受任务队列,然后加载任务队列,并且返回首个任务结果,不抛出异常。此方法返回与任务的call()方法返回的数据类型相同。这种情况下,返回String值。

下图显示本范例在控制台输出一个任务认证用户的执行信息:

pics/04_02.jpg

范例包括两个UserValidator对象返回随机布尔型值,每个UserValidator对象被TaskValidator类实现的Callable对象使用。如果UserValidator类的validate()方法放回fasle值,TaskValidator类抛出异常,否则返回true。

所以,有两个任务能够返回true值或者抛出异常,出现以下四种可能:

  • 两个任务均返回true值。invokeAny()方法的结果是在第一个完成的任务名称。
  • 第一个任务返回true,第二个任务抛出Exception。invokeAny()方法的结果是第一个任务的名称。
  • 第一个任务抛出Exception,第二个返回true值。nvokeAny()方法的结果是第二个任务的名称。
  • 两个任务均抛出Exception。这种情况下,invokeAny()方法抛出ExecutionException异常。

如果多次运行此范例,四个可能的结果都可能得到。

下图显示当两个任务抛出异常时,本范例在控制台的输出信息:

pics/04_03.jpg

扩展学习

ThreadPoolExecutor类提供了invokeAny()方法的另一种形式:

  • invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit) :如果未过指定时间,此方法执行所有的任务,且返回不抛出异常完成的第一个结果。TimeUnit是一个枚举类型的类,包含如下常量:DAYS、HOURS、MICROSECONDS、MILLISECONDS、MINUTES、NANOSECONDS、和SECONDS。

更多关注

  • 本章“运行多任务并处理所有结果”小节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值