Java多執行緒帶返回值的Callable介面

kaizi1992發表於2020-05-16

Java多執行緒帶返回值的Callable介面

在面試的時候,有時候是不是會遇到面試會問你,Java中實現多執行緒的方式有幾種?你知道嗎?你知道Java中有可以返回值的執行緒嗎?在具體的用法你知道嗎?如果兩個執行緒同時來呼叫同一個計算物件,計算物件的call方法會被呼叫幾次你知道嗎?如果這些你知道,那麼凱哥(凱哥Java:kaigejava)恭喜你,本文你可以不用看了。如果你不知道這些,那麼凱哥同樣要恭喜你,看了凱哥這篇文章之後,就知道這些了。來看看這篇文章我們能學到什麼

本節主要內容

一:三種獲取多執行緒的的寫法

二:分析第三種寫法的思想思路-使用了介面卡模式

三:第三種方法怎麼使用

四:多個執行緒呼叫同一個futrueTask後,future的call方法會被執行幾次?

一:三種獲取執行緒的寫法

我們已經知道Java中常用的兩種執行緒實現方式:分別是繼承Thread類和實現Runnable介面。

如下圖:

 

從上圖中,我們可以看到,第三種實現Callable介面的執行緒,而且還帶有返回值的。我們來對比下實現Runnable和實現Callable介面的兩種方式不同點:

1:需要實現的方法名稱不一樣:一個run方法,一個call方法

2:返回值不同:一個void無返回值,一個帶有返回值的。其中返回值的型別和泛型V是一致的。

3:異常:一個無需丟擲異常,一個需要丟擲異常。在後面使用場景中,凱哥會講解到的

二:callable介面的設計思路?

我們先來看看Thread類:這個類是Java中獲取線的物件。一般我們獲取並啟動執行緒呼叫的是start方。從JDK的API中,我們可以看到,start方法是JVM呼叫的

 

再來看看常寫的方法:

Thread t1 = new Thread();

t1.start();

我們來看看其構造器:

 

三個構造器:無參構造器、一個引數構造器和兩個引數構造器。但是就沒有我們Callable作為引數的構造器。那麼,我們想要獲取到執行緒,通過callable怎麼獲取呢 ?

 

就拿凱哥剛到帝都找房子的案例來說吧。凱哥剛到帝都人生地不熟的,想要找房子怎麼辦呢?

房東有房子,凱哥想要找房子,那麼這兩個本來沒有直接聯絡的通過房屋中介公司就產生了關係。凱哥要想找到房子,先要找到房屋中介公司,然後房屋中介公司又有房東的聯絡方式,然後凱哥就通過中介公司租到房東的房子了(中介公司從中間收取手續費)。這個現實案例我想大家都遇到過吧。

好了,我們通過上面案例在回到Thread類和Callable類來看,這兩個物件之間有沒有中間商呢?

 

從上圖中我們發現,Threa的有參構造都是Runnable介面的。那麼,有沒有一個類既實現了Runnable介面又實現了Callable介面呢?如果有這樣的一個類存在的話,callable就與Thread類產生了關係,就可以使用了。我們來看看Runnable介面的API吧

 

我們可以已知的子類有個RunnableFuture<V>。這個介面的形式和我們Callable介面的形式很像啊,如下圖:

 

我們從上圖對比中可以看到,兩個介面中的V都是方法返回值的型別。那麼Callable和Thread兩個類之間的橋樑就是這個類(RunnableFuture)或者是這個類的子類呢?我們接著來看看這個物件的子類。

 

其中SwingWorker這個我們不用看。這個是圖形化的Swing相關的。我們不用,那麼我們就來看看FutureTask這個類:

從這個類中,我們可以看到其實現了Runnable介面,在構造器中,我們可以看到:

FutureTask(Callable<V> callable)

建立一個 FutureTask ,它將在執行時執行給定的 Callable 。

如下圖:

 

這個類是不是既有Callable介面又有Runnable介面了?這個就是我們的中間類。

所以,我們通過上面分析就可以得到下圖的關係:

 

這種就是設計模式中的介面卡模式(PS:在後面,凱哥會重新分享23種設計模式的)。在Java中的中間商是不會賺取差價的,放心。O(∩_∩)O

三:callable怎麼使用及怎麼獲取返回值

知道了Callable的設計思路之後,那麼我們怎麼來使用呢?

步驟:

1:同樣建立一個類實現Callable介面;

2:通過futureTask類使用其傳遞Callable介面作為引數的有參構造方法;

3:使用thread的有參構造;

4:t1.start()啟動執行緒

5:啟動執行緒後,通過futureTask.get()方法獲取到執行緒的返回值。

如下圖:

 

我們來檢視執行結果:

 

進入了callable介面且獲取到了返回值:1024.說明callable的使用正確了。

需要注意:futrueTask.get()方法放到最後,這樣就不會影響主執行緒了。如果get方法放在前面的話,會造成主執行緒阻塞,等到futrueTask執行完成之後,才繼續執行自己的邏輯。這樣就失去了開啟執行緒的意義了!!!

四:多個執行緒同時呼叫結果

 

我們可以看到t1和t2都start了,說明兩個執行緒都啟動了。而且都是用的是同一個futureTask物件。問題:MyThread3中的call方法會被呼叫幾次呢?

 

相關文章