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()
排队的任务将导致调用运行任务的Thread
的UncaughtExceptionHandler
。 如果没有安装自定义处理程序,则默认的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的场景?
- 用try {} catch {}块代码embedded您的任务代码( Runnable或Callable实现)
- 实现
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
期望一个Runnable
而submit
可以采取Runnable
或Callable
作为参数(关于两者之间的差异更多信息 – 见下文)。 -
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。