如果返回值与我无关,我应该如何在ExecutorService的提交或执行之间做出选择?

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

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

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

当前回答

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

其他回答

摘自Javadoc:

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

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

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

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

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

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

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

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

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

By submitting a task (vs. executing it) you get back a future which can be used to get the result or cancel the action. You don't have this kind of control when you execute (because its return type id void) execute expects a Runnable while submit can take either a Runnable or a Callable as an argument (for more info about the difference between the two - see below). execute bubbles up any unchecked-exceptions right away (it cannot throw checked exceptions!!!), while submit binds any kind of exception to the future that returns as a result, and only when you call future.get() a the (wrapped) exception will be thrown . The Throwable that you'll get is an instance of ExecutionException and if you'll call this object's getCause() it will return the original Throwable.

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

即使要提交的任务不需要返回 结果,你仍然可以使用Callable<Void>(而不是使用Runnable)。 可以使用中断机制来取消任务。下面是如何实现取消策略的示例

总而言之,在Callable对象中使用submit(相对于在Runnable对象中使用execute)是一个更好的实践。我将引用Brian Goetz的《Java并发实践》:

6.3.2 Result-bearing tasks: Callable and Future The Executor framework uses Runnable as its basic task representation. Runnable is a fairly limiting abstraction; run cannot return a value or throw checked exceptions, although it can have side effects such as writing to a log file or placing a result in a shared data structure. Many tasks are effectively deferred computations—executing a database query, fetching a resource over the network, or computing a complicated function. For these types of tasks, Callable is a better abstraction: it expects that the main entry point, call, will return a value and anticipates that it might throw an exception.7 Executors includes several utility methods for wrapping other types of tasks, including Runnable and java.security.PrivilegedAction, with a Callable.

来自Javadoc:

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

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