首页 > 技术文章 > 源码:callable接口的底层实现

oyjg 2020-06-08 11:13 原文

在使用线程池的时候,我们需要使用到callable接口,那我们来看一下calllable的底层是怎么实现,并且有返回值的。

首先我们看一下调用。

 

 

 

 

ThreadPoolExcutor并没有实现submit方法,那么肯定是它的父类实现的。

 

 

 

 

 

如愿以偿在AbstractExecutorService中找到了submit方法,找到对应的方法,根据我们传入的callable接口找到。

 

 

 

在上面我们可以看到我们将callable或者runnable传入进去给我们返回了一个RunnableFuture,

实际上是返回了一个它的实现FutureTask,这就是核心类。

 

 

 

这里实际上做的是callable赋值,第一个构造就是直接赋值给callable就行,但是第二个构造方法是传入的runnable,并不能直接赋值,看看里面怎么处理的(runnable转成callable)

 

 

 

它实际上返回了一个RunnableAdapter

 

 

RunnableAdapter实现callable接口,所以能够赋值callable,构造方法只是内部赋值,重写callablecall方法,里面实际上就是将调用runnablerun方法。

这样就做到了callablerunnable接口的统一。

 

 

 

 

然后我们在看到

 

 

这里的execute需要传递runnable实例,但是我们都将他们处理成为callable了,其实这里我们传入的是FutureTask,也就是RunnableFuture的实现,而RunnableFuture实现了runnable接口,所以能够传入,而FutureTask重写了runnable接口的run方法。

 

 

这里调用了callablecall方法拿到了返回值。

然后我们梳理一下,  我们在runablerun方法中调用传入的callablecall方法,然后这里的callable我们是先将callable或者runable统一成callable

 

然后我们看看去拿到返回值的方法

 

 

因为实际上是返回的RunnableFuture的实现FutureTask,那么我们进入到FutureTaskget方法。

 

 

 

这里会有一个线程问题,但我们先不管,先看看是返回的什么。

这里返回的是outcome,其实这里的outcome就是前面callabe.call方法的返回值,

 

 

 

 

 

这样就将值返回出去了,然后我们再来看看线程问题。

因为这是一个多线程,然后我们通过get()方法得到返回值,那么其中就会有一个问题,那么就是有可能我们调用的get()方法,但是futureTaskrun方法并没有执行完,那么我们就取不到值,那我们来看看它是怎么保证能拿到值的。

 

 

这里做了一个判断,就是一个状态的判断,这里的state使用了volatile关键字保证了变量的可见性,防止另一个线程更改了状态值,而该线程未return出循环。

重点来了,这里是一个死循环,然后判断了一个状态,调用了LockSupport.park方法让这个线程阻塞,就是说你get的时候,如果我没执行完,那你就在这等着我执行完。

然后我们再来看看当run方法执行完的时候。

 

 

 

 

 

这里调用了一个LockSupport.unpark方法,然后传入了之前等待的线程,这样就能让那个线程同行,继续执行。

 

以上就是callable接口的核心流程,当然它内部还有许多判断,毕竟是工业级代码,但我们大概知道核心流程就ok了!

 

推荐阅读