ThreadPoolExecutor线程池中 submit和execute方法对比

本文代码使用 JDK1.8环境

ThreadPoolExecutor 的submit 和execute都可以提交一个任务(线程)到线程池中,那么这两个方法有什么不同呢?

笔者认为二者区别主要在以下2点:

1. submit 提交的任务可以获取执行结果,而execute 则不能

submit 方法会返回一个Future,通过Future.get()方法,可以得到执行结果。

execute 方法返回void

2. execute 会抛出异常,而submit 如果不获取执行结果的话会“吞掉”异常

2.1 只调用submit ()无法得到异常

比如下面的代码,我们期望打印出5行信息,如果发生异常,打印出异常堆栈信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static class ExceptionTask implements Runnable {
int a;
public ExceptionTask(int a) {
this.a = a;
}
@Override
public void run() {
if (a == 0)
// 如果为0,输出异常
throw new RuntimeException("exception in thread ");
} else {
System.out.println("线程 "+a+ " 执行");
}
}
}
public static void main(String[] args) throws Exception {
ThreadPoolExecutor pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
for (int i = 0; i < 5; i++) {
pool.submit(new ExceptionTask(i));
}
}

但是程序运行结束,打印除了4行信息,没有打印出异常堆栈信息。

2.2调用Future.get()可以得到运行异常

submit 方法会创建一个FutureTask 对象,FutureTask 的run方法,捕捉了异常,并且将异常放入了outcome 变量中,所以执行过程中发送异常不会抛出异常,在调用Future的get()方法时,会把outcome 中的异常返回回去。 调用流程如下:

  • 我们改一下上面的代码,调用future.get(),可正常获取异常

下面看一下源代码:

  • FutureTask.run() 源码
    其中一行代码setException(ex);将异常放入了outcome 变量中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public void run() {
    // ... some code ...
    try {
    Callable<V> c = callable;
    if (c != null && state == NEW) {
    V result;
    boolean ran;
    try {
    result = c.call();
    ran = true;
    } catch (Throwable ex) {
    result = null;
    ran = false;
    setException(ex);
    }
    if (ran)
    set(result);
    }
    } finally {
    // ... some code ...
    }
  • FutureTask.get()源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // get方法
    public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
    s = awaitDone(false, 0L);
    return report(s);
    }
    // 如果有异常,抛出异常
    private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
    return (V)x;
    if (s >= CANCELLED)
    throw new CancellationException();
    throw new ExecutionException((Throwable)x);
    }