深夜学习,发现ThreadPoolExecutor里面一个小知识点,故开热点连wifi怒写submit与execute方法的区别。

1.问题的来源

在看书的时候,涉及到java线程池问题的时候常常面临这样一个问题。当定义了一个Runnable对象想提交到线程池里面总是会看到不同的提交方法,产生的尬题如下:

public class ThreadPoolDemo {public static class MyTask implements Runnable{@Overridepublic void run() {System.out.println(System.currentTimeMillis()+":Thread ID:"+Thread.currentThread().getId());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args){MyTask task = new MyTask();ExecutorService es = Executors.newCachedThreadPool();for(int i=0;i<10;i++){es.submit(task);//问题出现在这里!es.execute(task);}}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

明明在看上一页书的时候,向线程池提交任务的时候,用的是submit()方法,等到看下一页的时候妈蛋,怎么用成execute()了,这两个搞什么鬼,同一个功能难道有两个方法可以调用?我不精陷入了深思,怒查java api文档。

2.java api文档对这两个方法的描述

首先,记忆里面对execute()方法是记忆比较深刻的,故查了一下该方法的api文档,发现信息如下:

  • execute() 是在Executor接口中定义的,ThreadPoolExecutor继承了AbstractExecutorService抽象类,该抽象类实现了ExecutorService接口(但并没有覆盖execute方法),而ExecutorService接口继承了Executor接口。

简而言之就是说ThreadPoolExecutor实现了execute()方法。然后我们来看看api文档对execute()方法是如何定义的:

execute public void execute(Runnable command) 
在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前 RejectedExecutionHandler处理。

参数: command - 要执行的任务。 抛出: RejectedExecutionException - 
如果无法接收要执行的任务,则由 RejectedExecutionHandler 决定是否抛出 
RejectedExecutionException NullPointerException - 如果命令为 null

看的是我一蒙一蒙的,主要是”在将来某个时间执行给定任务。”这一句让我很费解,所以我决定再看看submit()方法是怎么写的。

  • submit方法是ExecutorService接口里面定义的,具体的实现由AbstractExecutorService进行。

submit方法被重载了三次,分别对用三个不同的参数。对api摘录如下:

submit public Future<?> submit(Runnable task)

提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回null。

指定者: 接口 ExecutorService 中的 submit 参数: task - 要提交的任务 返回: 表示任务等待完成的 Future


submit public Future submit(Runnable task,T result) 提交一个 
Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果。

指定者: 接口 ExecutorService 中的 submit 参数: task - 要提交的任务 result - 返回的结果 
返回: 表示任务等待完成的 Future


submit public Future submit(Callable task) 
提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 
方法在成功完成时将会返回该任务的结果。 如果想立即阻塞任务的等待,则可以使用 result = 
exec.submit(aCallable).get(); 形式的构造。

注:Executors 类包括了一组方法,可以转换某些其他常见的类似于闭包的对象,例如,将 PrivilegedAction 转换为Callable 形式,这样就可以提交它们了。

指定者: 接口 ExecutorService 中的 submit 参数: task - 要提交的任务 返回: 表示任务等待完成的Future

如上所示,第二个与第三个可以理解,不就是我记录过的Future模式里面的那一套东西吗?就是说execute不支持Future这一套,而submit支持一套并可以返回一个Future给你到后面获取结果的时候可以get一get。

但是看到第一个的时候我又蒙蔽了,”提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回null。”妈蛋,那这样与execute又有什么区别呀。何必这样多此一举呢?我不服,我认为execute与submit里面肯定存在互相调用的关系,毕竟ExecutorService是Executor的子类嘛

3.怒开IDE,深入源码一探究竟

写了一个线程池,ctrl+左键深入execute方法,发现代码如下:

 public void execute(Runnable command) {if (command == null)throw new NullPointerException();/** Proceed in 3 steps:** 1. If fewer than corePoolSize threads are running, try to* start a new thread with the given command as its first* task.  The call to addWorker atomically checks runState and* workerCount, and so prevents false alarms that would add* threads when it shouldn't, by returning false.** 2. If a task can be successfully queued, then we still need* to double-check whether we should have added a thread* (because existing ones died since last checking) or that* the pool shut down since entry into this method. So we* recheck state and if necessary roll back the enqueuing if* stopped, or start a new thread if there are none.** 3. If we cannot queue task, then we try to add a new* thread.  If it fails, we know we are shut down or saturated* and so reject the task.*/int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))
                return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

这不是关于任务到线程池里面一些具体的操作吗!菜鸟太菜有些方法还是深入理解不了,不谈。回到刚刚想的那个问题,这样的话那么execute方法就是具体对任务的操作,那么submit方法呢?

点击进入了AbstractExecutorService抽象类源代码,发现源码如下:

    public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);execute(ftask);return ftask;}public <T> Future<T> submit(Runnable task, T result) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task, result);execute(ftask);return ftask;}public <T> Future<T> submit(Callable<T> task) {if (task == null) throw new NullPointerException();RunnableFuture<T> ftask = newTaskFor(task);execute(ftask);return ftask;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这妈蛋,不是都把拿到的Runnable任务都构造了RunnableFuture任务然后都抛给execute方法吗!也是醉了, 
得出结论1:如果提交的任务不需要一个结果的话直接用execute()会提升很多性能。

那我奇怪了newTaskFor这个又是什么jb玩意啊,用这个函数是怎么构造一个RunnableFuture任务的,怒气又来进入了方法,得结果如下:

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {return new FutureTask<T>(runnable, value);}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {return new FutureTask<T>(callable);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

骂了个比啊,这个方法不是帮你new了FutureTask吗!气得我得出 
结论二:就是相当于说如果你传的任务是需要结果的,那你就使用你的类去继承Callable接口,然后告诉submit方法就行了,如果你只需要一个特定的结果,就把那个特定的结果告诉submit方法然后把你想要的特定结果也告诉他,它只是帮你完成以前使用Future模式的时候你自己需要做的那些步骤而已,如果你不需要一个结果,那么就老老实实使用execute,如果你需要的是一个空结果,那么submit(yourRunnable)与submit(yourRunnable,null)是等价的!

Submit和execute的区别相关推荐

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

    深夜学习,发现ThreadPoolExecutor里面一个小知识点,故开热点连wifi怒写submit与execute方法的区别. 1.问题的来源 在看书的时候,涉及到java线程池问题的时候常常面临 ...

  2. ExecutorService中submit和execute的区别转

    在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用Thread的start()更好.在新特征中 ...

  3. 线程池的submit和execute方法区别

    线程池中的execute方法大家都不陌生,即开启线程执行池中的任务.还有一个方法submit也可以做到,它的功能是提交指定的任务去执行并且返回Future对象,即执行的结果.下面简要介绍一下两者的三个 ...

  4. ExecutorService中submit和execute的区别(转)

    在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用Thread的start()更好.在新特征中 ...

  5. java 线程池 execute_Java线程池的submit和execute方法区别

    线程池中的execute方法大家都不陌生,即开启线程执行池中的任务. 还有一个方法submit也可以做到,它的功能是提交指定的任务去执行并且返回Future对象,即执行的结果. 下面简要介绍一下两者的 ...

  6. 【转载保存】ExecutorService中submit和execute的区别

    https://www.cnblogs.com/wanqieddy/p/3853863.html

  7. 血的教训--如何正确使用线程池submit和execute方法

    血的教训之背景:使用线程池对存量数据进行迁移,但是总有一批数据迁移失败,无异常日志打印 凶案起因 ​ 听说parallelStream并行流是个好东西,由于日常开发stream串行流的场景比较多,这次 ...

  8. submit与execute区别

    (1)可以接受的任务类型 submit: execute: 可以看出: execute只能接受Runnable类型的任务 submit不管是Runnable还是Callable类型的任务都可以接受,但 ...

  9. java_多线程——线程池、submit和execute区别

    一.线程池的概念 顾名思义就是事先准备一个池子(线程池),初始化一些线程,当使用可以快速调用,不用再初始化线程,使用完成后,不再销毁该线程,归还到线程池,方便后面复用.节省创建和销毁线程资源,提高线程 ...

最新文章

  1. bose蓝牙音箱使用说明_身轻如燕好带走 时尚便携蓝牙音箱TOP5推荐
  2. 配置ORACLE 客户端连接到数据库
  3. hbase数据迁移到hive中
  4. JUnit5 TestSuite替代
  5. springboot中得注解_Spring以及SpringBoot中的常用的注解小结
  6. zabbix华为交换机模板_【教程】思科交换机镜像端口配置实例
  7. c++mfc编写实验_零基础学Windows窗口图形界面编程(不用MFC),菜鸟学完变身高手,敢与专业媲美...
  8. 数组tostring方法_数组toString()方法以及JavaScript中的示例
  9. php黄页,PHP 黄页的url
  10. mybais 之parameterType =list
  11. 洛谷P2278操作系统
  12. [leetcode]_Best Time to Buy and Sell Stock I II
  13. GDI+ 学习记录(23): 输出文本
  14. win32汇编 invoke 和 call区别
  15. JMeter 录制脚本
  16. 事件clientX、pageX、screenX、offsetX
  17. echarts百度地图涟漪效果
  18. 如何分割cue+ape、cue+flac、cue+wav、cue+dts整轨音乐文件
  19. Flutter 安装包体积优化
  20. LeetCode 371. 两整数之和(异或操作) / 639. 解码方法 II(动态规划)/ 437. 路径总和 III

热门文章

  1. mysql服务总是自动停止_关于Mysql服务自动停止的问题
  2. 【LOL盒子】-iOS自带下拉刷新控件【UIRefreshControl】
  3. 在IPAD2中使用VMware View Client
  4. Quartus II软件添加设备
  5. 近日国外的一名匿名网友提供了一张图片
  6. html之code标签和pre标签
  7. 一文带你全面了解,自动驾驶数据闭环之——大数据管理
  8. 项目中的自定义first table第一个表--人力资源考勤表
  9. centos7 的网卡配置文件设置bond接口. 如何固定mac地址?
  10. Linux安装JDK11、JDK8