在Java中,这段代码中的ExecutorService.submit和ExecutorService.execute有什么区别?

szqfcxe2  于 2023-05-05  发布在  Java
关注(0)|答案(9)|浏览(233)

我正在学习使用ExectorServicethreads和发送任务。我下面有一个简单的程序

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。但我看不出输出有什么不同。submitexecute方法有什么不同?这就是API说的
方法submit通过创建并返回一个Future来扩展基本方法Executor.execute(java.lang.Runnable),Future可用于取消执行和/或等待完成。方法invokeAny和invokeAll执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部任务完成。(类ExecutorCompletionService可用于编写这些方法的自定义变体。
但我不清楚它到底是什么意思?

carvr3hs

carvr3hs1#

正如你从JavaDoc中看到的,execute(Runnable)不返回任何东西。
但是,submit(Callable<T>)返回一个Future对象,它允许您稍后以编程方式取消正在运行的线程,并在Callable完成时获得返回的T。有关详细信息,请参见JavaDoc of Future

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

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

6ovsh4lw

6ovsh4lw2#

不同之处在于,execute直接启动任务,而submit返回一个Future对象来管理任务。您可以使用Future对象执行以下操作:

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

如果您向池提交Callable,则Future接口更有用。调用Future.get时,将返回call方法的返回值。如果不维护对Future的引用,则没有区别。

lymnna71

lymnna713#

execute:用于fire和forget呼叫
submit:使用它取消执行和/或等待已创建Future对象的完成
主要差异:Exception处理
submit()在框架本身中隐藏了未处理的Exception
execute()抛出未处理的Exception
submit()异常处理解决方案

  1. Package 您的Callable or Runnable code in try{} catch{} block

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

    1.实现您自己ThreadPoolExecutor并覆盖afterExecute方法
    相关文章代码示例:
    在ExecutorService的提交和ExecutorService的执行之间选择
    Future.get()将您的异步执行转换为同步执行,您将无法获得异步执行的好处。
    如果您的业务用例围绕异步事件处理,并且您期望在X秒内响应,那么您可以在Future操作上超时。
    关于旅游其他查询
    invokeAll:
    执行给定的任务,当所有任务完成或超时到期(以先发生者为准)时,返回一个Future列表,其中包含它们的状态和结果。
    invokeAny:
    执行给定的任务,如果在给定的超时时间过去之前有任何do,则返回已成功完成的任务的结果(即,没有抛出异常)。
    如果要等待所有提交的任务完成,请使用invokeAll
    如果您希望成功完成N个已提交任务中的一个,请使用invokeAny。在这种情况下,如果其中一个任务成功完成,则正在进行的任务将被取消。
qzwqbdag

qzwqbdag4#

submit()和execute()方法之间的主要区别是ExecuterService.submit()可以返回计算结果,因为它的返回类型是Future,但execute()方法不能返回任何东西,因为它的返回类型是void。Java 1.5的Executor框架中的核心接口是Executor接口,它定义了execute(Runnable task)方法,其主要目的是将任务与其执行分离。
提交给Executor的任何任务都可以由同一线程、线程池中的工作线程或任何其他线程执行。
另一方面,在ExecutorService接口中定义了submit()方法,该接口是Executor的子接口,并添加了终止线程池的功能,沿着添加了可以接受Callable任务并返回计算结果的submit()方法。

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

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

**除了submit()方法可以返回输出,execute()不能返回输出之外,以下是Java 5 Executor框架中这两个关键方法之间的显著区别。

  1. submit()可以接受Runnable和Callable任务,但execute()只能接受Runnable任务。
  2. submit()方法在ExecutorService接口中声明,而execute()方法在Executor接口中声明。
  3. submit()方法的返回类型是Future对象,但execute()方法的返回类型是void。
mepcadol

mepcadol5#

如果检查源代码,您将看到submitexecute的某种 Package 器

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

vngu2lb86#

Submit -返回Future对象,可用于检查已提交任务的结果。可用于取消或检查isDone等。
执行-不返回任何内容。

bmp9r5qi

bmp9r5qi7#

execute(Runnable command)是从接口Executor实现的方法。这意味着只执行命令,不返回任何内容。
ExecutorService有自己的启动任务的方法: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()

yqkkidmi

yqkkidmi8#

在之前的回答中,即

  • execute(..)运行任务并忘记它
  • submit(...)返回未来;

future的主要优点是可以设置超时。如果你有一个线程数量有限的执行器,并且你的执行永远都要花费时间,这可能会非常方便,它不会挂起进程。
例1:永远挂起并填充执行程序

ExecutorService executor = Executors.newFixedThreadPool(2);
  for (int i=0; i < 5; i++) {
     executor.execute(() -> {
         while (true) {
           System.out.println("Running...")
           Thread.sleep(Long.MAX_VALUE)
         }              
     });
  }

您的输出将是(即只有2个,它卡住了):

Running...
Running...

另一方面,你可以使用submit并添加一个超时:

ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i=0; i < 5; i++) {
    Future future = executor.submit(() -> {
        while (true) {
            System.out.println("Running...");
            Thread.sleep(Long.MAX_VALUE);
        }
    });

    try {
        future.get(1, TimeUnit.SECONDS);
    } catch (Exception e) {
        if (!future.isDone()) {
            System.out.println("Oops: " + e.getClass().getSimpleName());
            future.cancel(true);
        }
    }
}

输出将如下所示(注意执行器不会卡住,但您需要手动取消future):

Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
Running...
Oops: TimeoutException
7uhlpewt

7uhlpewt9#

基本上这两个调用都执行,如果你想要将来的对象,你应该在文档中调用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;
}

正如你所看到的,java除了调用run()方法之外,真的没有办法启动一个线程。因为我还发现Callable.call()方法在run()方法内部被调用。因此,如果对象是可调用的,它仍然会调用run()方法,反过来又会从doc调用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);
    }
}

相关问题