ExecutorService和TaskExecutor的区别和使用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ExecutorService和TaskExecutor的区别和使用相关的知识,希望对你有一定的参考价值。
参考技术A ExecutorService 和 TaskExecutor 都是使用线程池来管理多线程异步执行任务,他们有什么关联和区别,什么时候该用哪个呢?ExecutorService 属于 java.util.concurrent 包下面的,继承于 java.util.concurrent.Executor ,一般使用 java.util.concurrent.Executors 工厂类创建。 Executor 和 ExecutorService 都是 JDK 5 才提供的。
TaskExecutor 属于 org.springframework.core.task 包下面,也是继承与 java.util.concurrent.Executor
Java 1.7 引入了一种新的并发框架,主要用于实现“分而治之”的算法,特别是分治之后递归调用的函数,例如 quick sort 等。
ForkJoinPool 最适合的是计算密集型的任务,如果存在 I/O,线程间同步,sleep() 等会造成线程长时间阻塞的情况时,最好配合使用 ManagedBlocker。
具体请移步至
用于延时或者定期执行的异步任务/线程
提供线程池执行任务
ThreadPoolTaskExecutor 同样是提供线程池执行任务,但是可以使用xml或者JavaBean的形式进行配置,初始化。同样, ThreadPoolTaskExecutor 是使用 ThreadPoolExecutor 。即也是 ThreadPoolExecutor 的Spring包装。
ThreadPoolTaskScheduler 是 ScheduledThreadPoolExecutor 一个spring形式的包装。Spring中任务的调度使用的就是这个类,比如 @Scheduled
spring的 TaskExecutor 的两个常用实现类均是基于 Executor 实现类的包装,使其更加方便使用,更好的融入spring bean生态。
关于线程池的创建,拒绝策略,任务的执行状态,任务取消,等待任务完成等,网上资料很多了,将来有机会整理一下。
Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有啥区别?
【中文标题】Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有啥区别?【英文标题】:What is the difference between ExecutorService.submit and ExecutorService.execute in this code in Java?Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有什么区别? 【发布时间】:2013-09-14 20:01:27 【问题描述】:我正在学习使用ExectorService
来合并threads
并发送任务。我下面有一个简单的程序
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Processor implements Runnable
private int id;
public Processor(int id)
this.id = id;
public void run()
System.out.println("Starting: " + id);
try
Thread.sleep(5000);
catch (InterruptedException e)
System.out.println("sorry, being interupted, good bye!");
System.out.println("Interrupted " + Thread.currentThread().getName());
e.printStackTrace();
System.out.println("Completed: " + id);
public class ExecutorExample
public static void main(String[] args)
Boolean isCompleted = false;
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++)
executor.execute(new Processor(i));
//executor does not accept any more tasks but the submitted tasks continue
executor.shutdown();
System.out.println("All tasks submitted.");
try
//wait for the exectutor to terminate normally, which will return true
//if timeout happens, returns false, but this does NOT interrupt the threads
isCompleted = executor.awaitTermination(100, TimeUnit.SECONDS);
//this will interrupt thread it manages. catch the interrupted exception in the threads
//If not, threads will run forever and executor will never be able to shutdown.
executor.shutdownNow();
catch (InterruptedException e)
if (isCompleted)
System.out.println("All tasks completed.");
else
System.out.println("Timeout " + Thread.currentThread().getName());
它没有做任何花哨的事情,而是创建了两个threads
并总共提交了 5 个任务。在每个thread
完成其任务后,它会执行下一个,
在上面的代码中,我使用了executor.submit
。我也改成executor.execute
。但我看不出输出有什么不同。 submit
和 execute
方法有什么不同?
这就是API
所说的
方法 submit 通过创建和返回可用于取消执行和/或等待完成的 Future 扩展了基本方法 Executor.execute(java.lang.Runnable)。方法 invokeAny 和 invokeAll 执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成。 (类 ExecutorCompletionService 可用于编写这些方法的自定义变体。)
但我不清楚它的确切含义是什么?
【问题讨论】:
【参考方案1】:不同之处在于execute
只是简单地启动任务,而submit
返回一个Future
对象来管理任务。您可以使用Future
对象执行以下操作:
cancel
方法提前取消任务。
等待任务完成执行,使用get
。
如果您向池提交Callable
,Future
接口会更有用。调用Future.get
时会返回call
方法的返回值。如果您不维护对Future
的引用,则没有区别。
【讨论】:
不,execute() 规范说“在将来的某个时间执行给定的命令”。因此,它不一定立即开始。不同之处在于您不在乎何时执行或检查结果或完成等内容。【参考方案2】:正如您从 JavaDoc 中看到的那样,execute(Runnable)
没有返回任何内容。
但是,submit(Callable<T>)
返回一个Future
对象,它允许您稍后以编程方式取消正在运行的线程,并获取Callable
完成时返回的T
。详情请见JavaDoc of Future
Future<?> future = executor.submit(longRunningJob);
...
//long running job is taking too long
future.cancel(true);
此外,
如果 future.get() == null
并且没有抛出任何异常,则 Runnable 执行成功
【讨论】:
只是为了添加-> 一旦线程接了任务就不能取消了。虽然它会中断运行它的线程。 另一个区别是,如果使用execute(...)
运行的作业抛出异常,线程将在进程中被杀死,然后可能被ExecutorService
重新启动。【参考方案3】:
Submit - 返回 Future 对象,可用于检查提交任务的结果。可用于取消或检查 isDone 等。
执行 - 不返回任何内容。
【讨论】:
【参考方案4】:execute:
用它来打火就不用管了
submit:
使用它来检查方法调用的结果,并对调用返回的Future
对象采取适当的措施
主要区别:Exception
处理
submit()
在框架本身中隐藏未处理的Exception
。
execute()
抛出未处理的 Exception
。
用submit()
处理异常的解决方案
包装你的Callable or Runnable code in try catch block
或
保持future.get() call in try catch block
或
实现您自己的 ThreadPoolExecutor
并覆盖 afterExecute
方法
关于游览其他查询
invokeAll:
执行给定的任务,当全部完成或超时到期(以先发生者为准)时,返回保存其状态和结果的 Futures 列表。
invokeAny:
执行给定的任务,返回已成功完成的任务的结果(即不抛出异常),如果在给定的超时时间过去之前有任何操作。
如果您想等待所有提交的任务完成,请使用invokeAll
。
如果您希望成功完成 N 个已提交任务中的一项,请使用 invokeAny
。在这种情况下,如果其中一项任务成功完成,正在进行的任务将被取消。
带有代码示例的相关帖子:
Choose between ExecutorService's submit and ExecutorService's execute
【讨论】:
submit
不会“隐藏”异常,它会在调用 get
时将其抛出在 ExecutionException
中。然后可以通过ExecutionException.getCause()
检索原始异常
准确中肯的答案,@Ravindra 满分【参考方案5】:
基本上这两个调用都会执行,如果你想要未来的对象,你应该调用 submit() 方法 这里来自文档
public <T> Future<T> submit(Callable<T> task)
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
public <T> Future<T> submit(Runnable task, T result)
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
正如你所见,除了调用 run() 方法外,java 确实没有办法启动线程,IMO。因为我还发现在run()
方法中调用了Callable.call()
方法。因此,如果对象是可调用的,它仍然会调用run()
方法,而后者又会调用call()
方法
来自文档。
public void run()
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try
Callable<V> c = callable;
if (c != null && state == NEW)
V result;
boolean ran;
try
result = c.call();
ran = true;
catch (Throwable ex)
result = null;
ran = false;
setException(ex);
if (ran)
set(result);
finally
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
【讨论】:
【参考方案6】:submit() 和 execute() 方法的一个主要区别是 ExecuterService.submit() 可以返回计算结果,因为它的返回类型是 Future,但是 execute() 方法不能返回任何东西,因为它的返回类型是空白。 Java 1.5 Executor 框架中的核心接口是Executor 接口,它定义了execute(Runnable task) 方法,其主要目的是将任务与其执行分离。
提交给 Executor 的任何任务都可以由同一个线程、线程池中的工作线程或任何其他线程执行。
另一方面,submit()方法是在ExecutorService接口中定义的,它是Executor的子接口,增加了终止线程池的功能,同时增加了submit()方法,可以接受Callable任务和返回计算结果。
execute() 和 submit() 之间的相似之处:
-
submit() 和 execute() 方法都用于将任务提交到 Executor 框架以进行异步执行。
submit() 和 execute() 都可以接受 Runnable 任务。
您可以从 ExecutorService 接口访问 submit() 和 execute(),因为它还扩展了声明 execute() 方法的 Executor 接口。
除了 submit() 方法可以返回输出而 execute() 不能,以下是 Java 5 Executor 框架的这两个关键方法之间的其他显着区别。
-
submit() 可以同时接受 Runnable 和 Callable 任务,但 execute() 只能接受 Runnable 任务。
submit() 方法在 ExecutorService 接口中声明,而 execute() 方法在 Executor 接口中声明。
submit() 方法的返回类型是 Future 对象,但 execute() 方法的返回类型是 void。
【讨论】:
【参考方案7】:如果你查看源代码,你会发现submit
是execute
的一个包装器
public Future<?> submit(Runnable task)
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
【讨论】:
【参考方案8】:execute(Runnable command)
是从接口Executor
实现的方法。这意味着只执行命令并没有返回任何内容。
ExecutorService
有自己的启动任务方法:submit
、invokeAny
和invokeAll
,它们都以Callable
实例为主要目标。虽然有些方法以Runnable
作为输入,但实际上Runnable
将适应方法中的Callable
。为什么Callable
?因为我们可以在提交任务后得到Future<T>
的结果。
但是当您将Runnable
转换为Callable
时,您得到的结果就是您传递的值:
static final class RunnableAdapter<T> implements Callable<T>
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result)
this.task = task;
this.result = result;
public T call()
task.run();
return result;
那么,我们传递Runnable
来提交而不是在任务完成时获取结果有什么意义呢?因为有一个方法只有Runnable
作为参数,没有特定的结果。
阅读Future
的javadoc:
如果您想使用 Future 来取消可取消性但不提供可用的结果,您可以声明 Future> 形式的类型并返回 null 作为底层任务的结果。
所以,如果你只想执行Runnable
任务而不返回任何值,你可以使用execute()
。
如果你想运行Callable
任务,或者
如果您想以指定结果作为完成符号运行Runnable
任务,或者
如果你想运行一个任务并且可以取消它,
你应该使用submit()
。
【讨论】:
以上是关于ExecutorService和TaskExecutor的区别和使用的主要内容,如果未能解决你的问题,请参考以下文章
Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有啥区别?
ExecutorService——shutdown方法和awaitTermination方法
ExecutorService和ThreadPoolExecutor运行原理