2010-10-14 46 views
153

我应该如何之间的ExecutorService的submitexecute选择,如果返回值是不是我关心的?选择的ExecutorService的提交和ExecutorService的的执行

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

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

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

回答

166

有关异常/错误处理方面的差异。

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

在另一方面,由任务产生的Throwablesubmit()排队将Throwable绑定到从呼叫产生的submit()Future。在Future上调用get()将抛出ExecutionException与原始Throwable(原因可通过调用ExecutionException上的getCause()访问)。

+17

请注意,这种行为并不能保证,因为它取决于你的'Runnable'是否被包装在'Task'中或者你没有控制权。例如,如果你的'Executor'实际上是一个'ScheduledExecutorService',你的任务将被内部包装在一个'Future'中,并且未被捕获'Throwable's将被绑定到这个对象上。 – rxg 2013-06-13 08:55:07

+4

我的意思是'包裹在未来'或不'',当然。例如,查看Javadoc的[ScheduledThreadPoolExecutor#execute](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#execute(java.lang.Runnable)),例如。 – rxg 2013-06-13 09:28:43

7

从的Javadoc摘自:

方法submit通过创建和 返回{@link未来},可以是延伸的基方法{@link执行人#execute}用于取消执行和/或等待 完成。

我个人更喜欢使用执行,因为它感觉更具说明性,尽管这确实是个人偏好的问题。

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

submit呼叫由其母公司AbstractExecutorService提供,并且所有呼叫在内部执行。执行被直接覆盖/由ThreadPoolExecutor提供。

11

如果您不在意返回类型,请使用execute。这与提交一样,只是没有Future的回报。

+9

根据接受的答案,这是不正确的。异常处理是一个非常显着的差异。 – Zero3 2015-05-31 19:29:02

2

Javadoc

该命令可以在一个新的线程中执行,在一个线程池,或者调用线程,在Executor实现的自由裁量权。

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

34

执行:用它发射后不管的调用

提交:用它来检查方法调用的结果并采取Future适当行动反对通过调用返回

From 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 

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

替换

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) 

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

  1. 嵌入你的任务代码(无论Runnable或赎回实现)使用try {}赶上{}块代码
  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 
+0

毫无疑问,这里是最好的答案。 – smeeb 2016-07-28 09:34:16

+0

好脆解释。虽然扩展它不是真的需要。为了知道任务是否成功,只需要消耗未来的对象。因此,如果您打算使用Future(),则使用submit(),否则只需使用execute() – prash 2016-12-16 13:58:44

0

完整的答案是,在这里公布的两个答案的组成(加一点“额外”):

  • 提交任务(与执行它)你会得到一个未来,可以用来得到结果或取消行动。您没有这种控制,当你execute(因为它的返回类型ID void
  • execute需要一个Runnablesubmit可以采取无论是RunnableCallable作为参数(关于这两者之间的区别更多信息- 见下文)。
  • execute冒泡任何未检查到的例外马上(它不能抛出checked异常!!!),而submit结合任何类型的异常来返回结果的未来,只有当你调用一个future.get()的(包裹)异常将被抛出。您将得到的Throwable是ExecutionException的一个实例,如果您将此对象的getCause()它将返回原始的Throwable。

一些更多的(相关)点:

  • 即使要submit不需要返回 结果的任务,你仍然可以使用(使用一个Runnable代替)Callable<Void>
  • 取消任务可以使用interrupt机制完成。下面是如何实现的取消政策

综上所述an example,这是一个更好的做法是用Callable使用submit(对比executeRunnable)。我会从“实践中的Java并发”引述布赖恩戈茨:

6.3.2结果-bearing任务:可赎回和未来

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