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

时间:2010-10-14 01:10:59

标签: java multithreading executorservice

如果返回的值不是我关注的话,我该如何在 ExecutorService的 submitexecute之间进行选择?

如果我同时测试两者,除了返回的值之外,我没有看到两者之间有任何差异。

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

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

7 个答案:

答案 0 :(得分:194)

异常/错误处理方面存在差异。

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

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

答案 1 :(得分:57)

执行 :将其用于点火和忘记通话

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

来自javadocs

submit(Callable<T> task)

  

提交值返回任务以执行并返回Future       代表任务的待定结果。

Future<?> submit(Runnable task)

  

提交Runnable任务以执行并返回表示该任务的Future       任务。

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

submit()替换为execute():

,抛出相同的代码

替换

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()时处理这些类型的方案?

  1. 使用try {} catch {}阻止代码嵌入您的任务代码( Runnable或Callable实现)
  2. 实施CustomThreadPoolExecutor
  3. 新解决方案:

    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
    

答案 2 :(得分:11)

如果您不关心返回类型,请使用execute。它与提交相同,只是没有Future的回归。

答案 3 :(得分:7)

取自Javadoc:

  

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

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

提供更多信息:对于ExecutorService实施,调用Executors.newSingleThreadedExecutor()时返回的核心实施是ThreadPoolExecutor

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

答案 4 :(得分:2)

来自Javadoc

  

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

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

答案 5 :(得分:0)

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

  • 通过提交任务(与执行它),您将获得可用于获取结果或取消操作的未来。当execute(因为其返回类型为void
  • 时,您无法进行此类控制
  • execute预计Runnable submit可以RunnableCallable作为参数(有关两者之间差异的详细信息) - 见下文)。
  • execute立即冒泡任何未经检查的异常(它不能抛出已检查的异常!!!),而submit任何类型的异常绑定到将来返回因此,只有当您调用future.get()时,才会抛出(包装的)异常。您将获得的Throwable是ExecutionException的一个实例,如果您将此对象称为getCause(),它将返回原始的Throwable。

更多(相关)要点:

  • 即使您想要submit的任务不需要返回a 结果,您仍然可以使用Callable<Void>(而不是使用Runnable)。
  • 可以使用interrupt机制取消任务。这是an example如何实施取消政策

总而言之,将submitCallable(对executeRunnable)一起使用是一种更好的做法。我将在实践中引用Java并发性&#34;作者:Brian Goetz:

  

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

     

Executor框架使用Runnable作为其基本任务表示。 Runnable是公平的   限制抽象; run无法返回值或抛出已检查   例外,虽然它可能有副作用,如写入日志   将结果存档或放在共享数据结构中。很多任务都是   有效延迟计算 - 执行数据库查询,获取   通过网络的资源,或计算复杂的功能。对于   这些类型的任务,Callable是一个更好的抽象:它期望   主要入口点,呼叫,将返回一个值并预期   它可能会抛出异常.7执行器包括几个实用程序   包装其他类型任务的方法,包括Runnable和   java.security.PrivilegedAction,带有Callable。

答案 6 :(得分:0)

只需添加到接受的答案-

  

但是,任务抛出的异常会导致未捕获的异常   异常处理程序仅适用于通过execute()提交的任务;用于任务   使用submit()提交给执行者服务,抛出任何异常   被认为是任务返回状态的一部分。

Source