1. execute
和 submit
的区别
前面说了还需要介绍多线程中使用 execute
和 submit
的区别(这两个方法都是线程池 ThreadPoolExecutor
的方法)。
1.1 方法来源不同
execute
方法是线程池的顶层接口 Executor
定义的,在 ThreadPoolExecutor
中实现:
void execute(Runnable command);
submit()
是在ExecutorService
接口中定义的,并定义了三种重载方式:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
在AbstractExecutorService
类中有它们的具体实现,而ThreadPoolExecutor
继承了AbstractExecutorService
类。所以 ThreadPoolExecutor
也有这三个方法。
1.2 接收参数不同
从上面的方法来源中可以看出,二者接收参数类型不同:
execute()
方法只能接收实现Runnable
接口类型的任务submit()
方法则既可以接收Runnable
类型的任务,也可以接收Callable
类型的任务
1.3 返回值不同
由于 Runnable
和 Callable
的区别就是,Runnable
无返回值,Callable
有返回值。
所以 execute
和 submit
的返回值也不同。
-
execute()
的返回值是void
,线程提交后不能得到线程的返回值 -
submit()
的返回值是Future
,通过Future的get()
方法可以获取到线程执行的返回值,get()
方法是同步的,执行get()方法时,如果线程还没执行完,会同步等待,直到线程执行完成虽然submit()方法可以提交Runnable类型的参数,但执行Future方法的get()时,线程执行完会返回null,不会有实际的返回值,这是因为Runable本来就没有返回值
1.4 异常处理机制不同
- 用
submit
提交任务,任务内有异常也不会打印异常信息,而是调用get()
方法时,打印出任务执行异常信息 - 用
execute
提交任务时,任务内有异常会直接打印出来
后面源码分析中会体现这个不同点!
2. execute
和 submit
源码分析
2.1 submit
源码分析
submit
方法是 ExecutorService
接口定义,由 AbstractExecutorService
抽象类实现,有三个重载方法:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
可以看一下上面 submit
的三个重载方法,方法体很相似,都调用了一个方法 newTaskFor(...)
,那么就来看看这个方法,可以看到它有两个重载方法:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
解释一下上面两个重载方法吧:
- 第一个
newTaskFor(Runnable runnable, T value)
:可以看到它应该是将submit
方法传进来的Runnable
转化成了Callable
,并给一个返回值 - 第二个
newTaskFor(Callable<T> callable)
:就是submit
直接传进了一个Callable
,包装成FutureTask
返回。
上面代码中可以看一下 RunnableFuture
和 FutureTask
的关系:
先看一下 RunnableFuture:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
RunnableFuture 实现了 Runnable 和 Future,它的子类就是 FutureTask
public class FutureTask<V> implements RunnableFuture<V> {
// ...
}
到这里就明白了吧,当 submit 传入的参数是 Runnable 的时候,就需要 FutureTask的构造方法将 Runnable 转化成 Callable
下面看一下 FutureTask
的两个构造函数:
// 传入Runnable则是执行了一共方法,看一下这个方法,具体转化逻辑就有了
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
// 传入Callable直接赋值给类的成员变量
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
下面看一下当 submit
传入 Runnable
的时候,其实到这里就是调用了 FutureTask(Runnable r