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


193

如果返回值与我无关,我应该如何在ExecutorService的 Submitexecute之间进行选择?

如果同时测试两者,则除了返回的值外,我看不到其他任何差异。

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

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

Answers:


203

关于异常/错误处理有区别。

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

另一方面,Throwable与排队的任务所生成的submit()将绑定ThrowableFuture与调用所生成的submit()。调用get()时,Future将引发一个ExecutionException带有原始Throwable原因的(可通过调用getCause()来访问ExecutionException)。


19
请注意,此行为无法保证,因为它取决于您是否Runnable被包裹Task,您可能无法控制。例如,如果您Executor实际上是a ScheduledExecutorService,则您的任务将在内部包装在a中Future,而未捕获的Throwables将绑定到该对象。
rxg

4
我的意思是“包裹Future”。例如,请参阅Javadoc for ScheduledThreadPoolExecutor#execute
rxg

59

执行:将其用于火灾并忘记通话

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

来自javadocs

submit(Callable<T> task)

提交执行任务的返回值任务,并返回表示任务的未决结果的Future。

Future<?> submit(Runnable task)

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

void execute(Runnable command)

在将来的某个时间执行给定的命令。根据执行器的执行情况,命令可以在新线程,池线程或调用线程中执行。

使用时必须采取预防措施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 {}块代码嵌入您的Task代码(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

很好的解释。虽然扩展它不是真正需要的。为了了解任务是否成功,仅需要消耗将来的对象。因此,如果您打算使用Future <t>,请使用
commit

11

如果您不关心返回类型,请使用execute。它与提交相同,只是没有返回Future。


15
根据接受的答案,这是不正确的。异常处理是一个非常明显的区别。
Zero3,2015年

7

取自Javadoc:

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

就我个人而言,我更喜欢使用execute,因为它看起来更具声明性,尽管这实际上是个人喜好问题。

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

submit呼叫通过其母公司提供的AbstractExecutorService所有内部调用执行。ThreadPoolExecutor直接由execute覆盖/提供。


2

Javadoc

根据执行器的执行情况,命令可以在新线程,池线程或调用线程中执行。

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


1

完整答案由此处发布的两个答案组成(加上一些“额外”):

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

其他(相关)要点:

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

综上所述,submitCallable(相execute对于Runnable)一起使用是一种更好的做法。我将引用Brian Goetz的“实践中的Java并发性”:

6.3.2结果承担的任务:可调用的和将来的

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


1

只需添加到接受的答案中,

但是,任务抛出的异常仅针对通过execute()提交的任务进入未捕获的异常处理程序。对于使用Submit()提交给执行者服务的任务,任何引发的异常都被视为任务返回状态的一部分。

资源

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.