ExecutorService子线程返回值获取和exception捕获

来源:互联网 发布:刷视频点击率软件 编辑:程序博客网 时间:2024/06/05 04:55

最近在项目中需要使用多线程的并发来处理数据,刚开始希望使用static关键字来控制总的请求数,通过加减计数来进行数据请求的调度,本来并发量就不是很高,可是在实际的实验环境中,因为不能很好的控制请求的先后顺序,达到FIFO的要求,所以准备改善优化一下。对于线程池的使用不是很熟悉,通过网上的一些实例,结合自己的实际需求,对现成的一个实例,进行了优化,希望给各位有新的启发。

根据需求,主要做两方面的改正:

1.要对子线程的结果进行判断,这要求ExecutorService的执行结果必须有返回值

2.要捕获子线程的自定义异常。

首先第1点,获取任务的执行的返回值。

ExecutorService能够执行的任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的(<T> task) 方法来执行,并且返回一个<T><T>,是表示任务等待完成的 Future。故此选定实现接口为callable。


import java.util.Random;
import java.util.concurrent.Callable;

public class TaskWithResult implements Callable<String> {
    private int id;
    private static final String RESULT_NG_39 = "39";

    public TaskWithResult(int id) {
        this.id = id;
    }

    /**
     * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。
     *
     * @return
     * @throws Exception
     */
    public String call() throws TaskException {
        System.out.println("call()方法被自动调用,干活!!!             "
                + Thread.currentThread().getName());
        if (new Random().nextBoolean())

            // 此处为自定义的exception, 包含返回的error code,在实际的开发中,不同的错误会对应不同的code
            throw new TaskException("Meet error in task."
                    + Thread.currentThread().getName(), RESULT_NG_39);
        // 一个模拟耗时的操作
        for (int i = 999999999; i > 0; i--)
            ;
        // return "OK";
        return "call()方法被自动调用,任务的结果是:" + id + "    "
                + Thread.currentThread().getName();
    }
}
第二.要捕获子线程的自定义异常。

这个是很关键的一环,项目开发中会有各种类型的异常,各种不同的message,errorcode,能够分门别类的正确区分抛出,对以后维护调试起到事半功倍的作用。

自定义的异常类:

package threatPool;

public class TaskException extends Exception {

    /**
     *
     */
    private static final long serialVersionUID = 11583612349862927L;

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    private String errorCode;

    // 主要是希望通过errorCode的不同,区分不同业务段的不同error
    public TaskException(String message, String errorCode) {
        super(message);
        setErrorCode(errorCode);
    }
}
 最后,main函数调用:


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorServiceTest {
    
    private static final String RESULT_NG_39 = "39";
    
    public static void main(String[] args) throws TaskException {

        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<String>> resultList = new ArrayList<Future<String>>();

        // 创建10个任务并执行
        for (int i = 0; i < 10; i++) {
            // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
            TaskWithResult tr = new TaskWithResult(i);

            // 使用的submit,非excute方法
            Future<String> future = executorService.submit(tr);
            // 将任务执行结果存储到List中
            resultList.add(future);
        }
        executorService.shutdown();

        // 遍历任务的结果
        for (Future<String> fs : resultList) {
            try {

                // 打印各个线程(任务)执行的结果
                System.out.println(fs.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                // 获得子线程中抛出的异常code,注意此时使用的e.getCause(),对象的转型使用,我们在
                // 实际项目中不是简简单单的获得这个exception打印出来,而是通过这个exception获得更多的信息
                String errorCode = ((TaskException) e.getCause())
                        .getErrorCode();
                if(RESULT_NG_39.equals(errorCode)){
                    // log.info("XXXXXXXXXXXXXXXX");
                }
                // e.printStackTrace();
                return;
            }
        }
    }
}
执行结果:

call()方法被自动调用,干活!!!             pool-1-thread-1
call()方法被自动调用,干活!!!             pool-1-thread-2
call()方法被自动调用,干活!!!             pool-1-thread-3
call()方法被自动调用,干活!!!             pool-1-thread-5
call()方法被自动调用,干活!!!             pool-1-thread-4
call()方法被自动调用,干活!!!             pool-1-thread-6
call()方法被自动调用,干活!!!             pool-1-thread-7
call()方法被自动调用,干活!!!             pool-1-thread-8
call()方法被自动调用,干活!!!             pool-1-thread-10
call()方法被自动调用,干活!!!             pool-1-thread-9
call()方法被自动调用,任务的结果是:0    pool-1-thread-1
call()方法被自动调用,任务的结果是:1    pool-1-thread-2
the error code is 39 !
the error code is 39 !
call()方法被自动调用,任务的结果是:4    pool-1-thread-5
call()方法被自动调用,任务的结果是:5    pool-1-thread-6
call()方法被自动调用,任务的结果是:6    pool-1-thread-7
the error code is 39 !
call()方法被自动调用,任务的结果是:8    pool-1-thread-9
call()方法被自动调用,任务的结果是:9    pool-1-thread-10

感谢各位前人的demo,也希望给大家一些启示。



0 0