深夜学习,发现ThreadPoolExecutor里面一个小知识点,故开热点连wifi怒写submit与execute方法的区别。
1.问题的来源
在看书的时候,涉及到java线程池问题的时候常常面临这样一个问题。当定义了一个Runnable对象想提交到线程池里面总是会看到不同的提交方法,产生的尬题如下:
- 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方法,发现代码如下:
- 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抽象类源代码,发现源码如下:
- 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任务的,怒气又来进入了方法,得结果如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
骂了个比啊,这个方法不是帮你new了FutureTask吗!气得我得出
结论二:就是相当于说如果你传的任务是需要结果的,那你就使用你的类去继承Callable接口,然后告诉submit方法就行了,如果你只需要一个特定的结果,就把那个特定的结果告诉submit方法然后把你想要的特定结果也告诉他,它只是帮你完成以前使用Future模式的时候你自己需要做的那些步骤而已,如果你不需要一个结果,那么就老老实实使用execute,如果你需要的是一个空结果,那么submit(yourRunnable)与submit(yourRunnable,null)是等价的!