首先,我们来看下接口Executor.java的源码:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the <tt>Executor</tt> implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution.
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
还有继承Executor.java接口类的ExecutorService接口类源码:
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
从源码可以看到,execute(Runnable command)没有返回值,而submit(Runnable task)返回一个Future;所以最直观的区别就是返回值,那么具体是啥区别?
我们不妨看下Future的源码:
/**
* A <tt>Future</tt> represents the result of an asynchronous
* computation. Methods are provided to check if the computation is
* complete, to wait for its completion, and to retrieve the result of
* the computation. The result can only be retrieved using method
* <tt>get</tt> when the computation has completed, blocking if
* necessary until it is ready. Cancellation is performed by the
* <tt>cancel</tt> method. Additional methods are provided to
* determine if the task completed normally or was cancelled. Once a
* computation has completed, the computation cannot be cancelled.
* If you would like to use a <tt>Future</tt> for the sake
* of cancellability but not provide a usable result, you can
* declare types of the form {@code Future<?>} and
* return <tt>null</tt> as a result of the underlying task.
*/
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
/**
* Waits if necessary for the computation to complete, and then retrieves its result.
*/
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果。
我们来看下调用execute(Runnable x)的例子:
public static void main(String[] args) {
System.out.println("-------------start-------------");
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("执行线程任务");
}
});
System.out.println("-------------shutdown-------------");
executorService.shutdown();
System.out.println("-------------end-------------");
}
执行结果:
-------------start-------------
-------------shutdown-------------
-------------end-------------
执行线程任务
从执行结果看,有没有发现这几点:
- 代码中主线程并没有等待线程池执行完毕这一说,而是持续往下执行;
- 主线程和线程池之间没有直接关系,线程池使用自己的线程,生命周期也相互独立。
- shutdown()可以理解为主线程要求线程池关闭,但不会为此等待线程池执行完毕。
我们看下ExecutorService接口类中源码对shutdown方法的说明:
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException if a security manager exists and
* shutting down this ExecutorService may manipulate
* threads that the caller is not permitted to modify
* because it does not hold {@link
* java.lang.RuntimePermission}<tt>("modifyThread")</tt>,
* or the security manager's <tt>checkAccess</tt> method
* denies access.
*/
void shutdown();
是不是恍然大悟?原来在执行shutdown()方法时,它会有序地关闭掉先前提交的任务,但是它不会等待这些任务完成执行,除非执行awaitTermination方法(这个本章节暂时不去讨论了),所以也就有了再执行shutdown()方法后,没有等线程执行完毕,就去跑主线程了。
再看下调用submit(Runnable task)的例子:
public static void main(String[] args) {
System.out.println("-------------start-------------");
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<?> future = executorService.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("执行线程任务");
}
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("-------------shutdown-------------");
executorService.shutdown();
System.out.println("-------------end-------------");
}
执行结果:
-------------start-------------
执行线程任务
null
-------------shutdown-------------
-------------end-------------
从执行结果看,执行future.get()会让主线程一直等待,直到future获取到检索结果,也就是线程计算完成。
所以,我们可以总结下调用submit与execute区别:
- execute(Runnable x)没有返回值。可以执行任务,但无法判断任务是否成功完成。(异步执行)
- submit(Runnable task)返回一个Future。通过get等待直到获取到检索结果为止。(同步执行)