[笔记][Java7并发编程实战手册]4.5-4.6 运行多个任务并处理第一个结果/所有结果ThreadPoolExecutor

[笔记][Java7并发编程实战手册]系列目录


简介

  看到这个标题的时候,我也很纳闷,因为没有明白所表达的意思。
  ok,并发编程比较常见的一个问题是:当采用多个并发任务来解决一个问题的时候,往往只需要关心这个任务的第一个结果,例如:验证一个算法的时候,假如一个执行5个算法,那么最先返回结果的,就是最快的。

在本章将会学习,如何使用ThreadPoolExecutor来实现类似场景;

———-4.5 运行多个任务并处理所有结果
以下说明总结是后面一章的浓缩,因为没有什么可讲的了,就是一个方法的使用:
  等待任务结束的方法有:
    1. Future.isDone()判断任务状态,
    2. Future.get() 会阻塞直到任务结束
    3. ThreadPoolExecutor.shutdown(); 关闭执行器,等待所有任务结束。(纠正一下:该方法只是发起一个有序的关闭,并且不接受新任务),它能和awaitTermination(1, TimeUnit.DAYS)方法配合能达到阻塞并等待所有任务结束,在4.7中有详细示例

  上面的方法各有缺点:
    1. 只能判断任务的状态
    2. 只能对一个任务阻塞
    3. 只能通过关闭执行器和awaitTermination等待所有任务的结束
      
  所以4.5 运行多个任务并处理所有结果 章节中的方法就是使用invokeAll方法来等待一组任务的结束。其实大家可能都想到了,肯定是封装上面的单个处理。后面一章就不单独开章节了。


本章ThreadPoolExecutor使用心得

  1. 使用 ThreadPoolExecutor.invokeAny(list); 让线程池来帮我们拿到最快返回结果的结果。//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。如果此操作正在进行时修改了给定的 collection,则此方法的结果是不确定的。
  2. jdk 中说明了:任务结束(完成)的标识是:未抛出异常,正常返回;
  3. 拿到第一个结果后,执行器会取消未完成的任务,而在多线程中:当线程在活动之前或活动期间处于正在等待、休眠或占用状态且该线程被中断时,抛出InterruptedException异常
  4. 如果所有任务都抛出了异常,那么最终返回结果的时候也会抛出异常。(抛出的异常按照最后返回task)
  5. 还有一个invokeAll方法:当所有任务完成时返回所有任务的future列表。

示例

场景描述:以下程序来模拟在数据库中查找两个用户名(根据用户名和密码),获取最快查询到的用户。

/**
 * Created by zhuqiang on 2015/8/30 0030.
 */
public class Client {
    public static void main(String[] args) {
        ArrayList<Task> list = new ArrayList<Task>();
        list.add(new Task(new UserValidator(),"小强","123456"));
        list.add(new Task(new UserValidator(), "小小强", "123456"));
        list.add(new Task(new UserValidator(), "小小小小强", "123456"));

        ThreadPoolExecutor es = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
        try {
            String s = es.invokeAny(list);//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。如果此操作正在进行时修改了给定的 collection,则此方法的结果是不确定的。
            System.out.println(s + "   Main——用户验证通过");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        es.shutdown();
    }
}

// 用户验证对象,用来模拟在数据库中查询的过程
class UserValidator{
    public boolean validator(String name,String password){
        long  time = (long)(Math.random() * 10);
        try {
            TimeUnit.SECONDS.sleep(time);
            System.out.println("耗时=========" + Thread.currentThread().getName() + " 耗时:" + time);
        } catch (InterruptedException e) {
            System.out.println("取消=========" +Thread.currentThread().getName()+ " 该任务被中断");
            return false;
        }
//        return new Random().nextBoolean();  //返回一个随机值。表示是否验证通过的标识
        return true;  //始终返回true。用来验证,第一个被找到之后,后面的线程是否会被取消任务
    }
}

class Task implements Callable<String>{
    private UserValidator uv;
    private String name;
    private String password;

    public Task(UserValidator uv, String name, String password) {
        this.uv = uv;
        this.name = name;
        this.password = password;
    }

    @Override
    public String call() throws Exception {
        if (!uv.validator(name,password)){ //没有找到,并抛出一个异常
            System.out.println("没有找到用户========="  + "Task:" + Thread.currentThread().getName()  + "   " + name);
            throw new Exception("没有找到该用户:" + name);
        }
        System.out.println("找到用户========="  + "Task:" + Thread.currentThread().getName()  + "   " + name);
        return name;
    }
}

某一次运行结果:

耗时=========pool-1-thread-2 耗时:1
找到用户=========Task:pool-1-thread-2   小小强
小小强   Main——用户验证通过
取消=========pool-1-thread-2 该任务被中断
没有找到用户=========Task:pool-1-thread-2   小小小小强
取消=========pool-1-thread-1 该任务被中断
没有找到用户=========Task:pool-1-thread-1   小强

结果说明
1. 可以看到,第一个找到之后,结果就被返回了,并且后面未完成的任务被中断了。
2. 此示例的关键点es.invokeAny(list)

查看如果所有任务都没有返回结果呢,就是都抛出了异常
  修改上面的代码,让所有任务都抛出异常,会得到下面的某一次的运行结果:

耗时=========pool-1-thread-1 耗时:3
没有找到用户=========Task:pool-1-thread-1   小强
耗时=========pool-1-thread-1 耗时:0
没有找到用户=========Task:pool-1-thread-1   小小小小强
耗时=========pool-1-thread-2 耗时:6
java.util.concurrent.ExecutionException: java.lang.Exception: 没有找到该用户:小小强
没有找到用户=========Task:pool-1-thread-2   小小强
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at java.util.concurrent.AbstractExecutorService.doInvokeAny(AbstractExecutorService.java:193)
    at java.util.concurrent.AbstractExecutorService.invokeAny(AbstractExecutorService.java:215)
    at java7Concurrency.sync4_4.Client.main(Client.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.Exception: 没有找到该用户:小小强
    at java7Concurrency.sync4_4.Task.call(Client.java:60)
    at java7Concurrency.sync4_4.Task.call(Client.java:45)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值