1. 程式人生 > 其它 >從原始碼分析Runnable設計的缺陷及與Callable的區別

從原始碼分析Runnable設計的缺陷及與Callable的區別

Runnable 的缺陷

(1)不能拿到任務的返回結果

第一個缺陷,對於 Runnable 而言,不能拿到任務的返回結果,雖然可以利用其他的一些辦法,比如在 Runnable 方法中寫入日誌檔案或者修改某個共享的物件的辦法,來達到儲存執行緒執行結果的目的,但這種解決問題的行為效率著實不高。

實際上,在很多情況下執行一個子執行緒時,我們都希望能得到執行的任務的結果,也就是說,我們是需要得到返回值的,比如請求網路、查詢資料庫等。可是 Runnable 不能返回一個返回值,這是它第一個非常嚴重的缺陷。

(2)run()方法上不能丟擲 checked Exception

  在這段程式碼中,有兩個方法,第一個方法是一個普通的方法,叫作 normalMethod,可以看到,在它的方法簽名中有 throws Exception,並且在它的方法內也 throw 了一個 new IOException()。然後在下面的的程式碼中,我們新建了一個 Runnable 物件,同時重寫了它的 run 方法,我們沒有辦法在這個 run 方法的方法簽名上宣告 throws 一個異常出來。同時,在這個 run 方法裡面也沒辦法 throw 一個 checked Exception,除非如程式碼所示,用 try catch 包裹起來,但是如果不用 try catch 是做不到的。

為什麼有這樣的缺陷

  從原始碼可知,Runnable 是一個介面,並且裡面只有一個方法,叫作 public abstract void run()。這個方法已經規定了 run() 方法的返回型別是 void,而且這個方法沒有宣告丟擲任何異常。所以,當實現並重寫這個方法時,我們既不能改返回值型別,也不能更改對於異常丟擲的描述,因為在實現方法的時候,語法規定是不允許對這些內容進行修改的。

Runnable 為什麼設計成這樣

  假設 run() 方法可以返回返回值,或者可以丟擲異常,也無濟於事,因為我們並沒有辦法在外層捕獲並處理,這是因為呼叫 run() 方法的類(比如 Thread 類和執行緒池)是 Java 直接提供的,而不是我們編寫的。所以就算它能有一個返回值,我們也很難把這個返回值利用到,如果真的想彌補 Runnable 的這兩個缺陷,可以用下面的補救措施——使用 Callable。

Callable 介面

Callable 是一個類似於 Runnable 的介面,實現 Callable 介面的類和實現 Runnable 介面的類都是可以被其他執行緒執行的任務。 我們看一下 Callable 的原始碼:

  可以看出它也是一個 interface,並且它的 call 方法中已經聲明瞭 throws Exception,前面還有一個 V 泛型的返回值,這就和之前的 Runnable 有很大的區別。實現 Callable 介面,就要實現 call 方法,這個方法的返回值是泛型 V,如果把 call 中計算得到的結果放到這個物件中,就可以利用 call 方法的返回值來獲得子執行緒的執行結果了。

Callable 和 Runnable 的不同之處

(1)執行方法不同,兩個都是函式式介面,但Callable 規定的執行方法是 call(),而 Runnable 規定的執行方法是 run();

(2)能否拿到任務的返回結果,Callable 的任務執行後有返回值,而 Runnable 的任務執行後是沒有返回值的;

(3)是否可丟擲異常,call() 方法可丟擲異常,而 run() 方法是不能丟擲受檢查異常的;

(4)和 Callable 配合的有一個 Future 類,通過 Future 可以瞭解任務執行情況,或者取消任務的執行,還可獲取任務執行的結果,這些功能都是 Runnable 做不到的,Callable 的功能要比 Runnable 強大。

補充

實現Callable介面 和實現Runnable介面建立執行緒的相關例項請查閱我的另一篇文章——三種執行緒建立的方式

<END>

⭐️希望本文章對您有幫助,您的轉發、點贊是我創作的無限動力。

掃描下方二維碼關注微信公眾號,您會收到更多優質文章推送。

希望本文章對您有幫助,您的轉發、點贊是我的創作動力,十分感謝。更多好文推薦,請關注我的微信公眾號--JustJavaIt