一心多用多线程-细谈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的区别的主要内容,如果未能解决你的问题,请参考以下文章

Future配合线程池进行多线程任务并返回结果

Java线程池中的execute和submit

Java多线程:线程池详解

JVM细谈Java内存模型JMM

java线程池_submit()

Java线程池submit阻塞获取结果实现原理