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。但我看不出输出有什么不同。 submitexecute 方法有什么不同? 这就是API 所说的

方法 submit 通过创建和返回可用于取消执行和/或等待完成的 Future 扩展了基本方法 Executor.execute(java.lang.Runnable)。方法 invokeAny 和 invokeAll 执行最常用的批量执行形式,执行一组任务,然后等待至少一个或全部完成。 (类 ExecutorCompletionService 可用于编写这些方法的自定义变体。)

但我不清楚它的确切含义是什么?

【问题讨论】:

【参考方案1】:

不同之处在于execute 只是简单地启动任务,而submit 返回一个Future 对象来管理任务。您可以使用Future 对象执行以下操作:

使用cancel 方法提前取消任务。 等待任务完成执行,使用get

如果您向池提交CallableFuture 接口会更有用。调用Future.get时会返回call方法的返回值。如果您不维护对Future 的引用,则没有区别。

【讨论】:

不,execute() 规范说“在将来的某个时间执行给定的命令”。因此,它不一定立即开始。不同之处在于您不在乎何时执行或检查结果或完成等内容。【参考方案2】:

正如您从 JavaDoc 中看到的那样,execute(Runnable) 没有返回任何内容。

但是,submit(Callable&lt;T&gt;) 返回一个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】:

如果你查看源代码,你会发现submitexecute 的一个包装器

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 有自己的启动任务方法:submitinvokeAnyinvokeAll,它们都以Callable 实例为主要目标。虽然有些方法以Runnable 作为输入,但实际上Runnable 将适应方法中的Callable。为什么Callable?因为我们可以在提交任务后得到Future&lt;T&gt;的结果。

但是当您将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的理解和使用

ExecutorService——shutdown方法和awaitTermination方法

ExecutorService和ThreadPoolExecutor运行原理

ExecutorService和CompletionService区别

ExecutorService 和写入文件 [重复]