• 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. }
    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
  • 相关阅读:
    聊聊Docker数据卷和数据卷容器
    Nginx虚拟主机
    Shell文本处理四剑客
    Docker镜像、容器剖析
    Tomcat性能优化及JVM内存工作原理
    Nginx动静分离架构&&HA-LB集群整合
    Mysql主从复制架构实战
    Mysql编译安装详解
    Apache虚拟主机实战
    Ansible性能调优
  • 原文地址:https://www.cnblogs.com/jpfss/p/11192220.html
Copyright © 2020-2023  润新知