代码之家  ›  专栏  ›  技术社区  ›  Cheok Yan Cheng

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

  •  180
  • Cheok Yan Cheng  · 技术社区  · 14 年前

    我应该如何选择 执行人服务 submit execute ,如果返回值不是我关心的?

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

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

    ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
    threadExecutor.submit(new Task());
    
    6 回复  |  直到 7 年前
        1
  •  192
  •   Greg Mattes    11 年前

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

    排队的任务 execute() 会产生一些 Throwable 将导致 UncaughtExceptionHandler 对于 Thread 运行要调用的任务。默认值 未捕获的异常处理程序 ,通常打印 可抛出 堆栈跟踪 System.err 如果未安装自定义处理程序,则将调用。

    另一方面,A 可抛出 由排队的任务生成 submit() 将绑定 可抛出 Future 从呼叫到 提交() . 打电话 get() 在那 未来 会扔 ExecutionException 与原件 可抛出 作为其原因(通过呼叫 getCause() 执行例外 )

        2
  •  56
  •   Ravindra babu    8 年前

    执行 :使用它来点火,然后忘记通话

    提交 :使用它检查方法调用的结果并对 Future 被呼叫返回的对象

    javadocs

    submit(Callable<T> task)

    提交返回值的任务以供执行,并返回未来 表示任务的挂起结果。

    Future<?> submit(Runnable task)

    提交可运行的任务以执行,并返回表示该任务的未来 任务。

    void execute(Runnable command)
    

    将来某个时候执行给定的命令。命令可以在新线程、池线程或调用线程中执行,具体由执行器实现决定。

    在使用时必须采取预防措施 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 ()

    替换

    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块代码
    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
    
        3
  •  11
  •   Steven    14 年前

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

        4
  •  7
  •   Ravindra babu    8 年前

    取自JavaDoc:

    方法 submit 扩展基方法@link executor# execute }通过创建和 返回可用于取消执行和/或等待的@链接未来 完成。

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

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

    这个 提交 调用由其父级提供 AbstractExecutorService 所有调用都在内部执行。执行被重写/由提供 线程池 直接。

        5
  •  2
  •   Ravindra babu    8 年前

    Javadoc :

    命令可以在新线程、池线程或调用线程中执行,具体由执行器实现决定。

    所以取决于 Executor 您可能会发现在执行任务时提交线程会阻塞。

        6
  •  0
  •   Community CDub    7 年前

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

    • 通过提交一个任务(而不是执行它),你可以得到一个可以用来获得结果或取消操作的未来。当你 execute (因为它的返回类型ID void )
    • 执行 期望A Runnable 虽然 submit 可以选择 可运行的 或A Callable 作为一个论点(更多关于两者之间差异的信息,请参见下文)。
    • 执行 立即冒泡所有未检查的异常(它不能抛出已检查的异常!!!!),同时 提交 绑定 任何 一种对未来的例外,结果是,只有当你调用 future.get() 将引发(包装)异常。你会得到的被抛弃的是 ExecutionException 如果你称这个物体为 getCause() 它将返回原始的一次性文件。

    更多(相关)要点:

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

    总而言之,使用 提交 用一个 可赎回的 (VS) 执行 用一个 可运行的 )我将引用Brian Goetz在实践中的“Java并发”:

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

    执行器框架使用runnable作为其基本任务表示。runnable是一个公平的 限制抽象;运行不能返回值或引发检查 异常,尽管它可能有副作用,如写入日志 文件或将结果放入共享数据结构中。许多任务是 有效延迟计算执行数据库查询、获取 网络上的一种资源,或计算一个复杂的函数。为了 可调用的这些类型的任务是一个更好的抽象:它期望 主入口点call将返回一个值并预测 它可能引发异常。7个执行器包含多个实用程序 包装其他类型任务的方法,包括可运行和 java.security.privilegedAction,带有可调用的。