Submit和execute的区别

来源:互联网 发布:磁力解析软件 编辑:程序博客网 时间:2024/05/20 06:29

深夜学习,发现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);            }        }    }}
  • 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)是等价的!

原创粉丝点击