Java中此代码中的ExecutorService.submit和ExecutorService.execute有什么区别?

时间:2013-09-10 23:22:15

标签: java multithreading threadpool executorservice

我正在学习使用ExectorService汇集threads并发送任务。我有一个简单的程序

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


class Processor implements Runnable {

    private int id;

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

    public void run() {
        System.out.println("Starting: " + id);

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            System.out.println("sorry, being interupted, good bye!");
            System.out.println("Interrupted "+Thread.currentThread().getName());
            e.printStackTrace();    
        }

        System.out.println("Completed: " + id);
    }
}


public class ExecutorExample {

    public static void main(String[] args) {
        Boolean isCompleted=false;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        for(int i=0; i<5; i++) {
            executor.execute(new Processor(i));
        }

        //executor does not accept any more tasks but the submitted tasks continue
        executor.shutdown();

        System.out.println("All tasks submitted.");

        try {
            //wait for the exectutor to terminate normally, which will return true
            //if timeout happens, returns false, but this does NOT interrupt the threads
            isCompleted=executor.awaitTermination(100, TimeUnit.SECONDS);
            //this will interrupt thread it manages. catch the interrupted exception in the threads 
            //If not, threads will run forever and executor will never be able to shutdown.
            executor.shutdownNow();
        } catch (InterruptedException e) {
        }

        if (isCompleted){
        System.out.println("All tasks completed.");
       }
        else {
            System.out.println("Timeout "+Thread.currentThread().getName());
        }
    }
        }

它没有任何花哨,但创建了两个threads并总共提交了5个任务。每个thread完成任务后,需要下一个任务, 在上面的代码中,我使用executor.submit。我也改为executor.execute。但我没有看到输出有任何差异。 submit and execute方法在哪些方面有所不同? 这就是API所说的

  

方法提交通过创建和返回可用于取消执行和/或等待完成的Future来扩展基本方法Executor.execute(java.lang.Runnable)。方法invokeAny和invokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成。 (ExecutorCompletionService类可用于编写这些方法的自定义变体。)

但我不清楚它究竟意味着什么?

8 个答案:

答案 0 :(得分:45)

正如您从JavaDoc execute(Runnable)看到的那样,不返回任何内容。

但是,submit(Callable<T>)返回一个Future对象,允许您以后以编程方式取消正在运行的线程,并获取T时返回的Callable完成。有关详细信息,请参阅JavaDoc of Future

Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);

此外, 如果future.get() == null并且没有抛出任何异常,则Runnable成功执行

答案 1 :(得分:42)

不同之处在于execute只是简单地启动任务,而submit返回Future对象来管理任务。您可以使用Future对象执行以下操作:

  • 使用cancel方法提前取消任务。
  • 使用get等待任务完成执行。

如果您向池中提交FutureCallable界面会更有用。当您致电call时,将返回Future.get方法的返回值。如果您没有保留对Future的引用,则没有区别。

答案 2 :(得分:14)

execute:用它来点火和忘记电话

submit:用它来检查方法调用的结果,并对调用返回的Future对象采取适当的操作

主要区别:Exception处理

submit()隐藏了框架内未处理的Exception

execute()抛出未处理的Exception

使用submit()

处理例外的解决方案
  1. 包裹您的Callable or Runnable code in try{} catch{} block

    OR

  2. 保留future.get() call in try{} catch{} block

    OR

  3. 实施您自己的ThreadPoolExecutor并覆盖afterExecute方法

  4. 关于在

    上游览其他查询

    invokeAll

      

    执行给定的任务,返回一个Futures列表,其中包含所有完成或超时到期时的状态和结果,以先发生者为准。

    invokeAny

      

    执行给定的任务,返回已成功完成的任务的结果(即,不抛出异常),如果在给定的超时之前已经执行了任何操作。

    如果您想等待所有提交的任务完成,请使用invokeAll

    如果您正在寻找N个已提交任务中的一项任务的成功完成,请使用invokeAny。在这种情况下,如果其中一个任务成功完成,则将取消正在进行的任务。

    相关帖子,代码示例:

    Choose between ExecutorService's submit and ExecutorService's execute

答案 3 :(得分:5)

提交 - 返回Future对象,可用于检查已提交任务的结果。可用于取消或检查isDone等。

执行 - 不返回任何内容。

答案 4 :(得分:2)

submit()和execute()方法之间的主要区别在于ExecuterService.submit()可以返回计算结果,因为它具有Future的返回类型,但是execute()方法不能返回任何东西,因为它的返回类型是虚空。 Java 1.5的Executor框架的核心接口是Executor接口,该接口定义了execute(Runnable task)方法,其主要目的是将任务与其执行分离。

提交给执行器的任何任务都可以由同一线程,线程池中的工作线程或任何其他线程执行。

另一方面,在ExecutorService接口中定义了commit()方法,该接口是Executor的子接口,它添加了终止线程池的功能,以及添加了可接受Callable任务的Submit()方法和返回计算结果。

execute()和submit()之间的相似之处

  1. submit()和execute()方法都用于将任务提交给Executor框架以进行异步执行。
  2. submit()和execute()都可以接受可运行任务。
  3. 您可以从ExecutorService接口访问Submit()和execute(),因为它还扩展了声明了execute()方法的Executor接口。

除了commit()方法可以返回输出而execute()无法返回的事实之外,以下是Java 5的Executor框架的这两个关键方法之间的其他显着差异。

  1. submit()可以同时接受Runnable和Callable任务,但是execute()仅可以接受Runnable任务。
  2. 在ExecutorService接口中声明了commit()方法,而在Executor接口中声明了execute()方法。
  3. submit()方法的返回类型是Future对象,但是execute()方法的返回类型无效。

答案 5 :(得分:0)

基本上两个调用都执行,如果你想要将来的对象,你应该调用submit()方法  在文档中

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    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;
}

如您所见,除了调用run()方法IMO之外,java实际上没有其他方法可以启动线程。因为我还发现Callable.call()方法内部调用了run()方法。因此,如果对象是可调用的,它仍将调用run()方法,而该方法将依次调用call()方法 来自文档。

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    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 {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

答案 6 :(得分:0)

如果您检查源代码,您会发现submit有点像execute上的包装器

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

答案 7 :(得分:0)

execute(Runnable command)是接口Executor中的已实现方法。这意味着只执行命令,什么也不会返回。

ExecutorServic e具有自己的启动任务的方法:submitinvokeAnyinvokeAll,所有这些方法都以Callable实例为主要目标。尽管有些方法将Runnable作为输入,但是实际的Runnable将适用于该方法中的Callable。为什么Callable?因为提交任务后我们可以获得Future<T>的结果。

但是,当您将Runnable转换为Callable时,得到的结果就是您传递的值:

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

那么,我们通过Runnable提交而不是仅在任务完成时获得结果的意义何在?因为有一种方法仅以Runnable作为参数,而没有特定结果。

阅读Future的Javadoc:

如果出于可取消性的目的而使用Future而不提供可用的结果,则可以声明Future <?>形式的类型,并作为基础任务的结果返回null。

因此,如果您只想执行Runnable任务而没有返回任何值,则可以使用execute()

如果您想运行Callable任务,或者

如果要运行以指定结果作为完成符号的Runnable任务,或者

如果您想运行任务并可以取消任务,

您应该使用submit()