Java多線程帶返回值的Callable接口

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方法會被調用幾次呢?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章