203

如果返回值不是我关心的,我应该如何在ExecutorService 的 submitexecute之间进行选择?

如果我同时测试两者,除了返回值之外,我没有看到两者之间的任何差异。

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
4

7 回答 7

210

关于异常/错误处理存在差异。

排队的任务execute()生成一些Throwable将导致UncaughtExceptionHandler正在Thread运行的任务被调用。如果没有安装自定义处理程序,将调用默认值UncaughtExceptionHandler,通常将Throwable堆栈跟踪打印到。System.err

另一方面,Throwable队列中的任务生成的 asubmit()将绑定ThrowableFuture调用 to 生成的submit()。调用get()Future会抛出一个ExecutionException原始Throwable的作为它的原因(可以通过调用getCause()来访问ExecutionException)。

于 2010-10-21T10:33:59.973 回答
63

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

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

来自javadocs

submit(Callable<T> task)

提交一个返回值的任务以供执行,并返回一个表示该任务待处理结果的 Future。

Future<?> submit(Runnable task)

提交 Runnable 任务以执行并返回代表该任务的 Future。

void execute(Runnable command)

在将来的某个时间执行给定的命令。该命令可以在新线程、池线程或调用线程中执行,由 Executor 实现决定。

您在使用时必须采取预防措施submit()。除非您将任务代码嵌入try{} catch{}块中,否则它会在框架本身中隐藏异常。

示例代码:此代码将Arithmetic exception : / by zero.

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0

通过替换submit()execute() 抛出相同的代码:

代替

service.submit(new Runnable(){

service.execute(new Runnable(){

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)

使用 submit() 时如何处理这些类型的场景?

  1. 使用 try{} catch{} 块代码嵌入您的任务代码(Runnable 或 Callable 实现)
  2. 实施CustomThreadPoolExecutor

新解决方案:

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

class ExtendedExecutor extends ThreadPoolExecutor {

   public ExtendedExecutor() { 
       super(1,1,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
于 2016-02-16T05:29:30.527 回答
11

如果您不关心返回类型,请使用执行。和提交一样,只是没有Future的返回。

于 2010-10-14T02:09:21.007 回答
8

取自 Javadoc:

方法通过创建和返回可用于取消执行和/或等待完成的 {@link Future}submit扩展基本方法 {@link Executor# }。execute

就个人而言,我更喜欢使用 execute,因为它感觉更具声明性,尽管这确实是个人喜好问题。

提供更多信息:在ExecutorService实现的情况下,调用返回的核心实现Executors.newSingleThreadedExecutor()是一个ThreadPoolExecutor.

submit调用由其父级提供,AbstractExecutorService所有调用都在内部执行。execute 被ThreadPoolExecutor直接覆盖/提供。

于 2010-10-14T01:35:06.107 回答
2

Javadoc

该命令可以在新线程、池线程或调用线程中执行,由 Executor 实现决定。

因此,根据您的实现,Executor您可能会发现提交线程在任务执行时会阻塞。

于 2013-06-13T08:58:06.627 回答
2

完整答案是此处发布的两个答案的组合(加上一点“额外”):

  • 通过提交一个任务(相对于执行它),您可以返回一个未来,该未来可用于获取结果或取消操作。你没有这种控制权execute(因为它的返回类型 id void
  • execute期望一段Runnable时间submit可以将 aRunnable或 aCallable作为参数(有关两者之间差异的更多信息 - 见下文)。
  • execute立即冒泡任何未经检查的异常(它不能抛出已检查的异常!!!),同时将任何submit类型的异常绑定到作为结果返回的未来,并且只有当您调用(包装的)异常时才会被抛出。您将获得的 Throwable 是一个实例,如果您调用此对象,它将返回原始 Throwable。future.get()ExecutionExceptiongetCause()

还有一些(相关)点:

  • 即使您想要的任务submit不需要返回结果,您仍然可以使用Callable<Void>(而不是使用 a Runnable)。
  • 可以使用中断机制来取消任务。以下是如何实施取消政策的示例

总而言之,使用submita Callable(vs. executewith a Runnable) 是一种更好的做法。我将引用 Brian Goetz 的“Java concurrency in practice”:

6.3.2 结果承载任务:Callable 和 Future

Executor 框架使用 Runnable 作为其基本任务表示。Runnable 是一个相当有限的抽象;run 不能返回值或抛出已检查的异常,尽管它可能会产生副作用,例如写入日志文件或将结果放入共享数据结构中。许多任务实际上是延迟计算——执行数据库查询、通过网络获取资源或计算复杂的函数。对于这些类型的任务,Callable 是一个更好的抽象:它预计主入口点 call 将返回一个值并预计它可能会抛出异常。7 Executors 包含几个用于包装其他类型任务的实用方法,包括 Runnable和 java.security.PrivilegedAction,带有 Callable。

于 2016-04-29T21:55:49.947 回答
1

只是添加到接受的答案 -

但是,从任务中抛出的异常仅适用于使用 execute(); 提交的任务的未捕获异常处理程序;对于使用 submit() 提交给执行器服务的任务,任何抛出的异常都被视为任务返回状态的一部分。

来源

于 2019-11-26T13:54:04.157 回答