92

我正在学习使用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和方法有什么execute不同?这API就是说

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

但我不清楚它到底是什么意思?

4

8 回答 8

79

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

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

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

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

于 2013-09-10T23:32:11.693 回答
55

不同的是,它execute只是简单地启动任务,而submit返回一个Future对象来管理任务。您可以对对象执行以下操作Future

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

如果您将 a 提交到池中,该Future界面会更有用。调用时会返回方法Callable的返回值。如果您不维护对 的引用,则没有区别。callFuture.getFuture

于 2013-09-10T23:31:38.727 回答
31

execute:将其用于火灾并忘记呼叫

submit:Future使用它来检查方法调用的结果并对调用返回的对象采取适当的措施

主要区别:Exception处理

submit()Exception隐藏在框架本身中未处理。

execute()抛出未处理的Exception.

处理异常的解决方案submit()

  1. 包裹你的Callable or Runnable code in try{} catch{} block

    或者

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

    或者

  3. 实现你自己的ThreadPoolExecutor和覆盖afterExecute方法

关于旅游的其他疑问

调用全部

执行给定的任务,当全部完成或超时到期(以先发生者为准)时,返回保存其状态和结果的 Futures 列表。

调用任何

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

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

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

带有代码示例的相关帖子:

在 ExecutorService 的提交和 ExecutorService 的执行之间进行选择

于 2016-08-23T15:27:08.943 回答
9

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

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

另一方面,submit()方法定义在ExecutorService接口中,该接口是Executor的子接口,增加了终止线程池的功能,同时增加了submit()方法,可以接受Callable任务并返回结果的计算。

execute() 和 submit() 之间的相似之处还有:

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

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

  1. submit() 可以接受 Runnable 和 Callable 任务,但 execute() 只能接受 Runnable 任务。
  2. submit() 方法在 ExecutorService 接口中声明,而 execute() 方法在 Executor 接口中声明。
  3. submit() 方法的返回类型是 Future 对象,但 execute() 方法的返回类型是 void。
于 2019-01-24T22:12:44.097 回答
5

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

执行 - 不返回任何内容。

于 2015-07-08T12:47:36.380 回答
4

如果你检查源代码,你会发现这submit是一个包装器execute

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
于 2019-03-04T02:38:46.743 回答
1

execute(Runnable command)是从 Interface 实现的方法Executor。这意味着只需执行命令并没有返回任何内容。

ExecutorService有自己的启动任务的方法:,submit所有这些都以实例为主要目标。虽然有一些方法作为输入,但实际上会在方法中适应。为什么?因为我们可以在提交任务后得到一个结果。invokeAnyinvokeAllCallableRunnableRunnableCallableCallableFuture<T>

但是当您将 a 转换Runnable为 a时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;
    }
}

那么,我们传递 aRunnable来提交而不是只在任务完成时获取结果有什么意义呢?因为有一种方法只有Runnable作为参数而没有特定的结果。

阅读以下的 javadoc Future

如果您想使用 Future 来取消可取消性但不提供可用的结果,您可以声明 Future<?> 形式的类型并返回 null 作为底层任务的结果。

因此,如果您只想执行Runnable不返回任何值的任务,您可以使用execute().

如果你想运行一个Callable任务,或者

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

如果您想运行一项任务并能够取消它,

你应该使用submit().

于 2020-07-21T12:17:43.250 回答
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()从 doc 调用方法。

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);
    }
}
于 2018-07-06T11:33:20.407 回答