selectExecutorService的提交和ExecutorService的执行

如果返回的值不是我所关心的,我应该如何selectExecutorService的 提交还是执行 ?

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

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

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

exception/error handling有区别。

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

另一方面,由submit()排队的任务生成的Throwable会将Throwable绑定到通过调用submit()生成的Future 。 在Future上调用get()将会引发一个ExecutionException与原始的Throwable作为其原因(通过对ExecutionException调用getCause()来访问)。

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

提交 :使用它来检查方法调用的结果,并对由呼叫返回的Future objected采取适当的行动

从javadocs

submit(Callable<T> task)

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

Future<?> submit(Runnable task)

提交可执行任务并返回表示该任务的Future。

 void execute(Runnable command) 

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

使用submit()时你必须小心谨慎。 它隐藏了框架本身的exception,除非您将任务代码embedded到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 ()replacesubmit() ,抛出相同的代码:

更换

 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()时处理这些types的场景?

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

新解决scheme:

 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 

如果你不关心返回types,请使用execute。 就像提交一样,没有Future的回报。

采取从Javadoc:

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

我个人更喜欢使用执行,因为它感觉更具说明性,虽然这实际上是个人喜好的问题。

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

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

从Javadoc :

这个命令可以在一个新的线程中,在一个池中的线程中,或者在调用线程中执行,由Executor实现决定。

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

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

  • 通过提交一个任务(执行它),你可以得到一个未来,可以用来获得结果或取消行动。 你执行时没有这种控制(因为它的返回typesid为void
  • execute期望一个Runnablesubmit可以采取RunnableCallable作为参数(关于两者之间的差异更多信息 – 见下文)。
  • execute冒泡任何未经检查的exception马上(它不能抛出检查exception!!!),而submit绑定任何种类的exception,因此返回的未来,只有当你调用future.get()一个(包装)exception将被抛出。 你会得到的Throwable是一个ExecutionException的实例,如果你调用这个对象的getCause() ,它将返回原始的Throwable。

还有一些(相关)的观点:

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

总而言之,使用Callable submit是一个更好的做法(与使用Runnable execute相比)。 我将引用“实践中的Java并发”Brian Goetz:

6.3.2结果承载任务:Callable和Future

Executor框架使用Runnable作为其基本任务表示。 可运行是一个相当有限的抽象; 运行无法返回值或抛出检查exception,尽pipe它可能有副作用,如写入日志文件或将结果放入共享数据结构中。 许多任务是有效的延迟计算 – 执行数据库查询,通过networking获取资源或计算复杂的函数。 对于这些types的任务,Callable是一个更好的抽象:它期望主入口点call将返回一个值,并期望它可以抛出一个exception.7 Executors包含几个用于包装其他types任务的实用方法,包括Runnable和一个可调用的java.security.PrivilegedAction。