在使用線程池的時候,我們需要使用到callable接口,那我們來看一下calllable的底層是怎么實現,並且有返回值的。
首先我們看一下調用。
ThreadPoolExcutor並沒有實現submit方法,那么肯定是它的父類實現的。
如願以償在AbstractExecutorService中找到了submit方法,找到對應的方法,根據我們傳入的callable接口找到。
在上面我們可以看到我們將callable或者runnable傳入進去給我們返回了一個RunnableFuture,
實際上是返回了一個它的實現FutureTask,這就是核心類。
這里實際上做的是callable賦值,第一個構造就是直接賦值給callable就行,但是第二個構造方法是傳入的runnable,並不能直接賦值,看看里面怎么處理的(將runnable轉成callable)。
它實際上返回了一個RunnableAdapter
RunnableAdapter實現callable接口,所以能夠賦值callable,構造方法只是內部賦值,重寫了callable的call方法,里面實際上就是將調用runnable的run方法。
這樣就做到了callable和runnable接口的統一。
然后我們在看到
這里的execute需要傳遞runnable實例,但是我們都將他們處理成為callable了,其實這里我們傳入的是FutureTask,也就是RunnableFuture的實現,而RunnableFuture實現了runnable接口,所以能夠傳入,而FutureTask重寫了runnable接口的run方法。
這里調用了callable的call方法拿到了返回值。
然后我們梳理一下, 我們在runable的run方法中調用傳入的callable的call方法,然后這里的callable我們是先將callable或者runable統一成callable。
然后我們看看去拿到返回值的方法
因為實際上是返回的RunnableFuture的實現FutureTask,那么我們進入到FutureTask的get方法。
這里會有一個線程問題,但我們先不管,先看看是返回的什么。
這里返回的是outcome,其實這里的outcome就是前面callabe.call方法的返回值,
這樣就將值返回出去了,然后我們再來看看線程問題。
因為這是一個多線程,然后我們通過get()方法得到返回值,那么其中就會有一個問題,那么就是有可能我們調用的get()方法,但是futureTask的run方法並沒有執行完,那么我們就取不到值,那我們來看看它是怎么保證能拿到值的。
這里做了一個判斷,就是一個狀態的判斷,這里的state使用了volatile關鍵字保證了變量的可見性,防止另一個線程更改了狀態值,而該線程未return出循環。
重點來了,這里是一個死循環,然后判斷了一個狀態,調用了LockSupport.park方法讓這個線程阻塞,就是說你get的時候,如果我沒執行完,那你就在這等着我執行完。
然后我們再來看看當run方法執行完的時候。
這里調用了一個LockSupport.unpark方法,然后傳入了之前等待的線程,這樣就能讓那個線程同行,繼續執行。
以上就是callable接口的核心流程,當然它內部還有許多判斷,畢竟是工業級代碼,但我們大概知道核心流程就ok了!