在ExecutorService's提交和ExecutorService's执行之间进行选择

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

如果我对两者都进行测试,除了返回值之外,我没有看到两者之间有任何差异。

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

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
107327 次浏览

摘自Javadoc:

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

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

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

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

如果你不关心返回类型,使用execute。它和submit一样,只是没有Future的返回。

在异常/错误处理方面有区别。

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

另一方面,由带有submit()队列的任务生成的Throwable将把Throwable绑定到调用submit()产生的Future。在该Future上调用get()将抛出一个以原始Throwable为起因的ExecutionException(可通过在ExecutionException上调用getCause()来访问)。

Javadoc:

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

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

< em > < / em >执行:用于火灾和忘记呼叫

< em > < / em >提交:使用它检查方法调用的结果,并对调用返回的Future对象采取适当的操作

javadocs

submit(Callable<T> task)

提交一个返回值的任务来执行,并返回一个Future

.

.

.

Future<?> submit(Runnable task)

提交一个可运行任务以供执行,并返回表示该任务的Future 任务。< / p >

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

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)

如何处理这些类型的场景,而使用submit()?< / em >

  1. 嵌入你的任务代码(可运行或可调用实现),使用try{} catch{}块代码
  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,该future可用于获取结果或取消操作。当你execute时,你没有这种控制(因为它的返回类型id void)
  • execute期望Runnable,而submit可以接受RunnableCallable作为参数(有关两者之间差异的更多信息,请参阅下文)。
  • execute立即弹出任何未检查异常(它不能抛出检查异常!!),而submit任何类异常绑定到作为结果返回的future,并且只有在调用future.get() a时才会抛出(包装的)异常。你将得到的Throwable是ExecutionException的一个实例,如果你调用这个对象的getCause(),它将返回原始的Throwable。

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

  • 即使你想要submit的任务不需要返回a 结果,你仍然可以使用Callable<Void>(而不是使用Runnable)。李< / >
  • 可以使用中断机制来取消任务。这里是一个例子关于如何实现取消策略

总之,使用submitCallable (vs.使用executeRunnable)是更好的实践。我将引用Brian Goetz的《Java并发实践》:

6.3.2结果承载任务:可调用任务和未来任务

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

只是在公认的答案上加上

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

Source . abf819347906