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

Che*_*eng 187 java multithreading executorservice

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

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

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());
Run Code Online (Sandbox Code Playgroud)
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());
Run Code Online (Sandbox Code Playgroud)

小智 199

异常/错误处理有所不同.

与排队的任务execute()是产生一些Throwable将导致UncaughtExceptionHandlerThread正在运行的任务被调用.如果未安装自定义处理程序,则将调用默认值UncaughtExceptionHandler(通常将Throwable堆栈跟踪打印到)System.err.

另一方面,Throwable由排队的任务生成的submit()将绑定ThrowableFuture从调用生成的任务submit().调用get()Future会抛出一个ExecutionException与原来的Throwable(通过调用访问作为其事业getCause()上的ExecutionException).

  • 请注意,这种行为无法保证,因为它取决于您的`Runnable`是否包含在`Task`中,您可能无法控制.例如,如果你的`Executor`实际上是一个`ScheduledExecutorService`,你的任务将在内部包含在`Future`中并且未被捕获`Throwable将被绑定到这个对象. (19认同)
  • 当然,我的意思是"包裹在'未来'中".有关[ScheduledThreadPoolExecutor #execute](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#execute(java.lang.Runnable)),请参阅Javadoc,例如. (4认同)

Rav*_*abu 59

执行:用于火灾和忘记呼叫

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

来自javadocs

submit(Callable<T> task)

提交值返回任务以执行并返回表示任务的挂起结果的Future.

Future<?> submit(Runnable task)

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

void execute(Runnable command)
Run Code Online (Sandbox Code Playgroud)

在将来的某个时间执行给定的命令.该命令可以在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0
Run Code Online (Sandbox Code Playgroud)

通过替换submit()execute()抛出相同的代码:

更换

service.submit(new Runnable(){
Run Code Online (Sandbox Code Playgroud)

service.execute(new Runnable(){
Run Code Online (Sandbox Code Playgroud)

输出:

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)
Run Code Online (Sandbox Code Playgroud)

如何在使用submit()时处理这些类型的场景?

  1. 使用try {} catch {}块代码嵌入您的任务代码(Runnable或Callable实现)
  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);
   }
 }
Run Code Online (Sandbox Code Playgroud)

输出:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
Run Code Online (Sandbox Code Playgroud)


Ste*_*ven 11

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

  • 根据公认的答案,这是不正确的.异常处理是一个非常重要的区别. (12认同)

Syn*_*tax 7

取自Javadoc:

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

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

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

submit呼叫通过其母公司提供的AbstractExecutorService所有内部调用执行.执行被ThreadPoolExecutor直接覆盖/提供.


alf*_*sin 5

完整答案由此处发布的两个答案组成(加上一点“额外”):

\n\n
    \n
  • 通过提交任务(相对于执行任务),您可以获得一个 future,可用于获取结果或取消操作。当你execute(因为它的返回类型 idvoid
  • \n
  • execute预计需要一段Runnable时间submit,可以采取 aRunnable或 aCallable作为参数(有关两者之间差异的更多信息 - 请参阅下文)。
  • \n
  • execute立即冒出任何未检查的异常(它不能抛出检查的异常!!!),同时将任何submit类型的异常绑定到作为结果返回的未来,并且只有当您调用(包装的)异常时才会抛出。您将获得的 Throwable 是一个实例,如果您调用该对象的future.get()ExecutionExceptiongetCause()它将返回原始 Throwable。
  • \n
\n\n

还有一些(相关的)点:

\n\n
    \n
  • 即使您想要的任务submit不需要返回结果,您仍然可以使用Callable<Void>(而不是使用Runnable)。
  • \n
  • 可以使用中断机制来取消任务。以下是如何实施取消政策的示例
  • \n
\n\n

submit总而言之,使用a是更好的做法Callable(相对于execute使用 a Runnable)。我将引用 Brian Goetz 的《Java 并发实践》:

\n\n
\n

6.3.2 结果承载任务:Callable 和 Future

\n\n

Executor 框架使用 Runnable 作为其基本任务表示。Runnable 是一个相当有限的抽象;run 无法返回值或抛出检查异常,尽管它可能会产生副作用,例如写入日志文件或将结果放入共享数据结构中。许多任务都有效地推迟了计算,例如执行数据库查询、通过网络获取资源或计算复杂的函数。对于这些类型的任务,Callable 是一个更好的抽象:它预计主入口点 call 将返回一个值,并预计它可能抛出异常。7 执行器包含几个实用程序方法,用于使用 Callable 包装其他类型的任务,包括 Runnable 和 java.security.PrivilegedAction。

\n
\n