execute和submit的区别与联系

Posted jpfss

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了execute和submit的区别与联系相关的知识,希望对你有一定的参考价值。

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. private static void initExecutors()
  10. if (executor.isTerminated())
  11. executor = Executors.newCachedThreadPool();
  12. private static void waitToTerminated()
  13. executor.shutdown();
  14. while (!executor.isTerminated())
  15. /**
  16. * 测试 submit(Callable<T> task)
  17. *
  18. * @param callable
  19. * @param <T>
  20. * @return
  21. */
  22. public static <T> T testSubmitCallable(Callable callable)
  23. Future<T> future = executor.submit(callable);
  24. T result = null;
  25. try
  26. result = future.get();
  27. catch (InterruptedException e)
  28. e.printStackTrace();
  29. catch (ExecutionException e)
  30. e.printStackTrace();
  31. return result;
  32. /**
  33. * 测试submit(Runnable task, T result)
  34. *
  35. * @param runnable
  36. * @param t
  37. * @param <T>
  38. * @return
  39. */
  40. public static <T> T testSubmitRunnable(Runnable runnable, T t)
  41. Future<T> future = executor.submit(runnable, t);
  42. T result = null;
  43. try
  44. result = future.get();
  45. catch (InterruptedException e)
  46. e.printStackTrace();
  47. catch (ExecutionException e)
  48. e.printStackTrace();
  49. return result;
  50. /**
  51. * 测试 submit(Runnable task)
  52. * submit提交Runnable任务会默认返回null
  53. *
  54. * @param runnable
  55. * @return
  56. */
  57. public static Object testSubmitRunnable(Runnable runnable)
  58. Future<?> future = executor.submit(runnable);
  59. Object v = null;
  60. try
  61. v = future.get();
  62. catch (InterruptedException e)
  63. e.printStackTrace();
  64. catch (ExecutionException e)
  65. e.printStackTrace();
  66. return v;
  67. /**
  68. * 测试 execute(Runnable command)
  69. * execute会直接抛出异常,submit只有通过调用Future对象的get方法才能获取异常
  70. *
  71. * @param runnable
  72. */
  73. public static void testExecuteRunnable(Runnable runnable)
  74. executor.execute(runnable);

这个测试框架提供了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. return sum;
  8. /**
  9. * 会抛异常的CallableTask
  10. */
  11. class ExceptionCallableTask implements Callable<Boolean>
  12. public Boolean call() throws Exception
  13. int num = 1 / 0;
  14. return false;
  15. class RunnableTask implements Runnable
  16. @Override
  17. public void run()
  18. System.out.println("I am a runnable task");
  19. /**
  20. * 会抛异常的RunnableTask
  21. */
  22. class ExceptionRunableTask implements Runnable
  23. @Override
  24. public void run()
  25. int num = 1 / 0;

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

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();

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

  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();

控制台输出:

  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();

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();
  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. return result;

当我们在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();

如果将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

以上是关于execute和submit的区别与联系的主要内容,如果未能解决你的问题,请参考以下文章

多线程 submit与execute区别

一心多用多线程-细谈java线程池submit与execute的区别

UDP和TCP协议之间的联系与区别及他们与IP协议的联系是啥?要详细的

Bagging与Boosting的联系与区别

RTD与PT100的关系 RTD与PT100之间的联系和区别是啥呢?

DOS中XCOPY与COPY 的联系与区别是啥,如何使用