欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解线程池execute和submit用法

程序员文章站 2022-05-31 10:21:40
在使用线程池时,我们都知道线程池有两种提交任务的方式,那么他们有什么区别呢? 1.execute提交的是Runnable类型的任务,而submit提交的是Callable或者Runnable类型的任务 2.execute的提交没有返回值,而submit的提交会返回一个Future类型的对象 3.ex ......

在使用线程池时,我们都知道线程池有两种提交任务的方式,那么他们有什么区别呢?

 

1.execute提交的是runnable类型的任务,而submit提交的是callable或者runnable类型的任务

2.execute的提交没有返回值,而submit的提交会返回一个future类型的对象

3.execute提交的时候,如果有异常,就会直接抛出异常,而submit在遇到异常的时候,通常不会立马抛出异常,而是会将异常暂时存储起来,等待你调用future.get()方法的时候,才会抛出异常

 

了解了以上区别以后,我们再想想,他们之间会不会有联系呢?答案是肯定的,其实在submit里面的,任务提交的时候,底层都是使用execute来提交的,我们先来看看源码

1     public <t> future<t> submit(callable<t> task) {
2         if (task == null) throw new nullpointerexception();
3         runnablefuture<t> ftask = newtaskfor(task);
4         execute(ftask);
5         return ftask;
6     }

上面的代码中,将 task 传进一个newtaskfor方法以后,返回一个runnablefuture对象,然后就直接将这个对象传入execute方法里面了,所以跟据上面的代码,可以总结出三点:

1.runnalbefuture 是一个实现了runnable接口的类

2.newtaskfor方法能将callable任务转换成runnable任务

3.通过返回类型可以判断,runnablefuture也实现了future接口,并且由于在方法外,可以通过future获得值这一点看,runnablefuture可以保存值

现在我们来看看具体是怎么实现的

首先看看futurerunnable的run方法,因为他是runnable任务,被execute提交后肯定会运行这个任务的run方法

 1 public void run() {
 2         if (state != new ||
 3             !unsafe.compareandswapobject(this, runneroffset,
 4                                          null, thread.currentthread()))
 5             return;
 6         try {
 7             callable<v> c = callable;
 8             if (c != null && state == new) {
 9                 v result;
10                 boolean ran;
11                 try {
12                     result = c.call();
13                     ran = true;
14                 } catch (throwable ex) {    //捕获所有异常
15                     result = null;
16                     ran = false;
17                     setexception(ex);       //有异常就保存异常
18                 }
19                 if (ran)
20                     set(result);             //没有异常就设置返回值
21             }
22         } finally {
23             // runner must be non-null until state is settled to
24             // prevent concurrent calls to run()
25             runner = null;
26             // state must be re-read after nulling runner to prevent
27             // leaked interrupts
28             int s = state;
29             if (s >= interrupting)
30                 handlepossiblecancellationinterrupt(s);
31         }
32     }

可以看到,runnable 的 run 方法里,直接调用了他封装的 callable 任务的 call()方法 ,如果有异常,就直接将异常放入 这个类的静态变量里 ,如果没有异常,就将返回值放入自己的局部变量里,我们来看看上面代码中的set(result)方法吧

1       protected void set(v v) {
2         if (unsafe.compareandswapint(this, stateoffset, new, completing)) {
3             outcome = v;
4             unsafe.putorderedint(this, stateoffset, normal); // final state
5             finishcompletion();
6         }
7     }

可以看到标红的部分,其实就是将传进来的值,保存到一个叫做 outcome 的静态变量里面了,而相对应的,由于一开始提交任务时返回了本类的引用(future对象),所以可以通过引用访问静态变量的方式,访问到返回值了,future.get() 在runnablefuture中的实现如下

1     public v get() throws interruptedexception, executionexception {
2         int s = state;
3         if (s <= completing)  //如果线程还没运行完成
4             s = awaitdone(false, 0l);  //阻塞等待
5         return report(s);      //返回值或者异常
6     }

上面代码中的report(s)实现如下:

    private v report(int s) throws executionexception {
        object x = outcome;      
        if (s == normal)        
            return (v)x;
        if (s >= cancelled)
            throw new cancellationexception();
        throw new executionexception((throwable)x);
    }

 现在我们再总结一下吧:首先callable没有什么神奇之处,通过submit提交之后,会被包装成runnablefuture,同时被当作返回值传回,在runnablefuture的run方法中,会调用它保存的callable任务的call方法,同时跟据是否有异常,来决定保存返回值或者异常到其静态变量中,最后外部通过get方法就可以访问到返回值啦!

小技巧:在使用future.get() 获取返回值的时候,如果当前这个值还没有计算出来,那么就会产生阻塞,直到获取到值,这个时候我们可以用 future.isdone 检查任务的状态,再根据状态去get这个值,这样就不会阻塞了

请尊重作者的辛苦付出,如果转载请注明出处,谢谢