一心多用多线程-细谈java线程池submit与execute的区别
Posted hello_读书就是赚钱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一心多用多线程-细谈java线程池submit与execute的区别相关的知识,希望对你有一定的参考价值。
深夜学习,发现ThreadPoolExecutor里面一个小知识点,故开热点连wifi怒写submit与execute方法的区别。
1.问题的来源
在看书的时候,涉及到java线程池问题的时候常常面临这样一个问题。当定义了一个Runnable对象想提交到线程池里面总是会看到不同的提交方法,产生的尬题如下:
public class ThreadPoolDemo
public static class MyTask implements Runnable
@Override
public 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);
明明在看上一页书的时候,向线程池提交任务的时候,用的是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);
这不是关于任务到线程池里面一些具体的操作吗!菜鸟太菜有些方法还是深入理解不了,不谈。回到刚刚想的那个问题,这样的话那么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;
这妈蛋,不是都把拿到的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);
骂了个比啊,这个方法不是帮你new了FutureTask吗!气得我得出
结论二:就是相当于说如果你传的任务是需要结果的,那你就使用你的类去继承Callable接口,然后告诉submit方法就行了,如果你只需要一个特定的结果,就把那个特定的结果告诉submit方法然后把你想要的特定结果也告诉他,它只是帮你完成以前使用Future模式的时候你自己需要做的那些步骤而已,如果你不需要一个结果,那么就老老实实使用execute,如果你需要的是一个空结果,那么submit(yourRunnable)与submit(yourRunnable,null)是等价的!
以上是关于一心多用多线程-细谈java线程池submit与execute的区别的主要内容,如果未能解决你的问题,请参考以下文章