从ExecutorService源码中看调用submit与execute区别

首先,我们来看下接口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-------------
执行线程任务

从执行结果看,有没有发现这几点:

  1. 代码中主线程并没有等待线程池执行完毕这一说,而是持续往下执行;
  2. 主线程和线程池之间没有直接关系,线程池使用自己的线程,生命周期也相互独立。
  3. 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等待直到获取到检索结果为止。(同步执行)

转载于:https://my.oschina.net/lienson/blog/1518717

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值