Java 在ExecutorService和x27之间进行选择;s提交并执行服务';执行

Java 在ExecutorService和x27之间进行选择;s提交并执行服务';执行,java,multithreading,executorservice,Java,Multithreading,Executorservice,如果返回的值不是我所关心的,我应该如何在执行服务的之间进行选择 如果我同时测试这两个,除了返回值之外,我看不到它们之间有任何差异 ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); threadExecutor.execute(new Task()); 摘自Javadoc: 方法submit通过创建和 返回可用于取消执行和/或等待的{@link Future} 完成 就个人而言,我更喜欢使用execute

如果返回的值不是我所关心的,我应该如何在执行服务的之间进行选择

如果我同时测试这两个,除了返回值之外,我看不到它们之间有任何差异

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


摘自Javadoc:

方法
submit
通过创建和 返回可用于取消执行和/或等待的{@link Future} 完成

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

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


submit
调用由其父级
AbstractExecutorService
提供,所有调用都在内部执行。execute由
线程池执行器直接重写/提供。

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

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

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

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

该命令可以在新线程、池线程或调用线程中执行,具体取决于执行器实现

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

执行器:将其用于激发和忘记调用

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

submit(可调用任务)

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

未来提交(可运行任务)

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

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

使用
submit()
时必须采取预防措施。它在框架本身中隐藏异常,除非您将任务代码嵌入
try{}catch{}
块中

示例代码:此代码将
算术异常:/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
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)
java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
submit()
替换为
execute
(),会引发相同的代码:

替换

service.submit(new Runnable(){

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0
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)
java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
如何在使用submit()时处理这些类型的场景?

  • 用try{}catch{}块代码嵌入任务代码(可运行或可调用实现)
  • 实施
    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);
       }
     }
    

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

    • 通过提交任务(与执行任务相比),您可以获得一个未来,该未来可用于获得结果或取消操作。当执行
      时,您没有这种控制(因为它的返回类型id
      void
    • execute
      需要
      Runnable
      ,而
      submit
      可以将
      Runnable
      Callable
      作为参数(有关两者之间差异的更多信息,请参阅下文)
    • execute
      立即弹出任何未检查的异常(它不能抛出已检查的异常!!!),而
      submit
      将任何类型的异常绑定到作为结果返回的未来,并且只有在调用
      future.get()
      a时,(包装的)异常才会被抛出。您将获得的Throwable是
      ExecutionException
      的一个实例,如果调用此对象的
      getCause()
      ,它将返回原始Throwable
    还有几个(相关)要点:

    • 即使要提交的任务不需要返回 结果,您仍然可以使用
      Callable
      (而不是使用
      Runnable
    • 可以使用该机制取消任务。下面是如何实施取消策略的示例
    总之,使用
    submit
    Callable
    (与使用
    Runnable
    execute
    相比)是一种更好的做法。我将引用Brian Goetz的“Java并发在实践中”一文:

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

    Executor框架使用Runnable作为其基本任务表示。Runnable是一个相当不错的选择 限制抽象;run无法返回值或抛出已选中的值 异常,尽管它可能有副作用,例如写入日志 文件或将结果放置在共享数据结构中。许多任务都很复杂 有效的