execute和submit都属于线程池的方法,execute只能提交Runnable类型的任务,而submit既能提交Runnable类型任务也能提交Callable类型任务。

execute会直接抛出任务执行时的异常,submit会吃掉异常,可通过Future的get方法将任务执行时的异常重新抛出。

execute所属顶层接口是Executor,submit所属顶层接口是ExecutorService,实现类ThreadPoolExecutor重写了execute方法,抽象类AbstractExecutorService重写了submit方法。

submit和execute由于参数不同有四种实现形式,如下所示,本文主要研究这四种形式在各自使用场景下的区别和联系


  1. <T> Future<T> submit(Callable<T> task);
  2. <T> Future<T> submit(Runnable task, T result);
  3. Future<?> submit(Runnable task);
  4. void execute(Runnable command);

关于Runnable和Callable任务如果你还存在疑惑,建议你先看看我的上篇文章Runnable和Callable的区别和联系

测试代码的整体框架如下:


  1. import java.util.concurrent.*;
  2. public class TestSubmitAndExecute {
  3. static ExecutorService executor = Executors.newCachedThreadPool();
  4. public static void main(String[] args) {
  5. initExecutors();
  6. /**put test codes here*/
  7. /***/
  8. waitToTerminated();
  9. }
  10. private static void initExecutors() {
  11. if (executor.isTerminated()) {
  12. executor = Executors.newCachedThreadPool();
  13. }
  14. }
  15. private static void waitToTerminated() {
  16. executor.shutdown();
  17. while (!executor.isTerminated()) {
  18. }
  19. }
  20. /**
  21. * 测试 submit(Callable<T> task)
  22. *
  23. * @param callable
  24. * @param <T>
  25. * @return
  26. */
  27. public static <T> T testSubmitCallable(Callable callable) {
  28. Future<T> future = executor.submit(callable);
  29. T result = null;
  30. try {
  31. result = future.get();
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. } catch (ExecutionException e) {
  35. e.printStackTrace();
  36. }
  37. return result;
  38. }
  39. /**
  40. * 测试submit(Runnable task, T result)
  41. *
  42. * @param runnable
  43. * @param t
  44. * @param <T>
  45. * @return
  46. */
  47. public static <T> T testSubmitRunnable(Runnable runnable, T t) {
  48. Future<T> future = executor.submit(runnable, t);
  49. T result = null;
  50. try {
  51. result = future.get();
  52. } catch (InterruptedException e) {
  53. e.printStackTrace();
  54. } catch (ExecutionException e) {
  55. e.printStackTrace();
  56. }
  57. return result;
  58. }
  59. /**
  60. * 测试 submit(Runnable task)
  61. * submit提交Runnable任务会默认返回null
  62. *
  63. * @param runnable
  64. * @return
  65. */
  66. public static Object testSubmitRunnable(Runnable runnable) {
  67. Future<?> future = executor.submit(runnable);
  68. Object v = null;
  69. try {
  70. v = future.get();
  71. } catch (InterruptedException e) {
  72. e.printStackTrace();
  73. } catch (ExecutionException e) {
  74. e.printStackTrace();
  75. }
  76. return v;
  77. }
  78. /**
  79. * 测试 execute(Runnable command)
  80. * execute会直接抛出异常,submit只有通过调用Future对象的get方法才能获取异常
  81. *
  82. * @param runnable
  83. */
  84. public static void testExecuteRunnable(Runnable runnable) {
  85. executor.execute(runnable);
  86. }
  87. }

这个测试框架提供了4个静态方法用来测试submit和execute总共包含的四种表现形式,除此之外提供initExecutors用于提前检测线程池是否终止,若终止则初始化,waitToTerminated方法用于关闭线程池,并阻塞到线程池终止为止。

除了测试框架之外提供了4个不同的任务,分别测试Callable和Runnable在抛异常时的表现形式。


  1. class CallableTask implements Callable<Integer> {
  2. @Override
  3. public Integer call() throws Exception {
  4. int sum = 0;
  5. for (int i = 0; i < 520; i++) {
  6. sum += i;
  7. }
  8. return sum;
  9. }
  10. }
  11. /**
  12. * 会抛异常的CallableTask
  13. */
  14. class ExceptionCallableTask implements Callable<Boolean> {
  15. public Boolean call() throws Exception {
  16. int num = 1 / 0;
  17. return false;
  18. }
  19. }
  20. class RunnableTask implements Runnable {
  21. @Override
  22. public void run() {
  23. System.out.println("I am a runnable task");
  24. }
  25. }
  26. /**
  27. * 会抛异常的RunnableTask
  28. */
  29. class ExceptionRunableTask implements Runnable {
  30. @Override
  31. public void run() {
  32. int num = 1 / 0;
  33. }
  34. }

整体结构搭起来,下来就是研究具体差异的时刻了。

1)首先研究Future<?> submit(Runnable task)和void execute(Runnable command),这两个方法都是执行Runnable类型任务,前者有返回值,但是返回值为null,后者无返回值。


  1. public static void main(String[] args) {
  2. initExecutors();
  3. /**put test codes here*/
  4. Object object = testSubmitRunnable(new RunnableTask());
  5. System.out.println(object);
  6. testExecuteRunnable(new RunnableTask());
  7. /***/
  8. waitToTerminated();
  9. }

很容易观察控制台输出如下:


  1. I am a runnable task
  2. null
  3. I am a runnable task

可以看出submit执行Runnable类型任务时默认返回值为null。如果我们需要submit在提交Runnable任务可以返回非空,就需要用到submit的另外一个重载的方法:<T> Future<T> submit(Runnable task, T result);

2)submit(Runnable task, T result) 方法可以使submit执行完Runnable任务后返回指定的返回值。

main方法如下:


  1. public static void main(String[] args) {
  2. initExecutors();
  3. /**put test codes here*/
  4. // Object object = testSubmitRunnable(new RunnableTask());
  5. // System.out.println(object);
  6. //
  7. // testExecuteRunnable(new RunnableTask());
  8. Integer i = testSubmitRunnable(new RunnableTask(), 3);
  9. System.out.println(i);
  10. Boolean bool = testSubmitRunnable(new RunnableTask(), true);
  11. System.out.println(bool);
  12. String str = testSubmitRunnable(new RunnableTask(), "你好吗");
  13. System.out.println(str);
  14. /***/
  15. waitToTerminated();
  16. }

控制台输出:


  1. I am a runnable task
  2. 3
  3. I am a runnable task
  4. true
  5. I am a runnable task
  6. 你好吗

可以看出我们输入的什么参数,任务执行完毕后就返回什么参数。

3)submit(Callable<T> task)这个方法没什么好说的,用来提交Callable类型任务,返回值由call方法决定。

main方法如下:


  1. public static void main(String[] args) {
  2. initExecutors();
  3. /**put test codes here*/
  4. // Object object = testSubmitRunnable(new RunnableTask());
  5. // System.out.println(object);
  6. //
  7. // testExecuteRunnable(new RunnableTask());
  8. // Integer i = testSubmitRunnable(new RunnableTask(), 3);
  9. // System.out.println(i);
  10. //
  11. // Boolean bool = testSubmitRunnable(new RunnableTask(), true);
  12. // System.out.println(bool);
  13. //
  14. // String str = testSubmitRunnable(new RunnableTask(), "你好吗");
  15. // System.out.println(str);
  16. Object o = testSubmitCallable(new CallableTask());
  17. System.out.println(o);
  18. /***/
  19. waitToTerminated();
  20. }

CallableTask的执行逻辑是计算0到520之间的所有整数之和,所以控制台输出:

134940

4)关于execute和submit遭遇异常的表现

execute直接将任务执行时期的异常抛出,main方法和控制台打印分别如下:


  1. public static void main(String[] args) {
  2. initExecutors();
  3. /**put test codes here*/
  4. // Object object = testSubmitRunnable(new RunnableTask());
  5. // System.out.println(object);
  6. //
  7. // testExecuteRunnable(new RunnableTask());
  8. // Integer i = testSubmitRunnable(new RunnableTask(), 3);
  9. // System.out.println(i);
  10. //
  11. // Boolean bool = testSubmitRunnable(new RunnableTask(), true);
  12. // System.out.println(bool);
  13. //
  14. // String str = testSubmitRunnable(new RunnableTask(), "你好吗");
  15. // System.out.println(str);
  16. // Object o = testSubmitCallable(new CallableTask());
  17. // System.out.println(o);
  18. testExecuteRunnable(new ExceptionRunableTask());
  19. /***/
  20. waitToTerminated();
  21. }

  1. Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
  2. at ExceptionRunableTask.run(TestRunnableAndCallable.java:38)
  3. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  4. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  5. at java.lang.Thread.run(Thread.java:745)

submit比较特殊,如果没有通过Future.get来获取结算结果,则吃掉异常。先将测试方法稍做调整,修改成如下形式:


  1. /**
  2. * 测试 submit(Callable<T> task)
  3. *
  4. * @param callable
  5. * @param <T>
  6. * @return
  7. */
  8. public static <T> T testSubmitCallable(Callable callable) {
  9. Future<T> future = executor.submit(callable);
  10. T result = null;
  11. /*
  12. try {
  13. result = future.get();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. } catch (ExecutionException e) {
  17. e.printStackTrace();
  18. }
  19. */
  20. return result;
  21. }

当我们在main方法添加如下代码时,控制台其实没有打印任何异常


  1. public static void main(String[] args) {
  2. initExecutors();
  3. /**put test codes here*/
  4. // Object object = testSubmitRunnable(new RunnableTask());
  5. // System.out.println(object);
  6. //
  7. // testExecuteRunnable(new RunnableTask());
  8. // Integer i = testSubmitRunnable(new RunnableTask(), 3);
  9. // System.out.println(i);
  10. //
  11. // Boolean bool = testSubmitRunnable(new RunnableTask(), true);
  12. // System.out.println(bool);
  13. //
  14. // String str = testSubmitRunnable(new RunnableTask(), "你好吗");
  15. // System.out.println(str);
  16. // Object o = testSubmitCallable(new CallableTask());
  17. // System.out.println(o);
  18. // testExecuteRunnable(new ExceptionRunableTask());
  19. testSubmitCallable(new ExceptionCallableTask());
  20. /***/
  21. waitToTerminated();
  22. }

如果将testSubmitCallable代码中被注释的部分取消注释,则可以看到异常信息如下:


  1. java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
  2. at java.util.concurrent.FutureTask.report(FutureTask.java:122)
  3. at java.util.concurrent.FutureTask.get(FutureTask.java:192)
  4. at TestSubmitAndExecute.testSubmitCallable(TestSubmitAndExecute.java:58)
  5. at TestSubmitAndExecute.main(TestSubmitAndExecute.java:28)
  6. Caused by: java.lang.ArithmeticException: / by zero
  7. at ExceptionCallableTask.call(TestRunnableAndCallable.java:20)
  8. at ExceptionCallableTask.call(TestRunnableAndCallable.java:18)
  9. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  10. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  11. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  12. at java.lang.Thread.run(Thread.java:745)

关于execute和submit的简单研究到此结束,谢谢观看。

原文地址:https://www.jianshu.com/p/29610984f1dd

05-20 12:51