人物背景:
老徐,男,本名徐福貴,從事Java相關研發工作多年,職場老油條,摸魚小能手,雖然歲數不大但長的比較著急,人稱老徐。據說之前炒某幣敗光了所有家產,甚至現在還有欠債。
阿珍,女,本名陳家珍,剛剛入職不久的實習生,雖然是職場菜鳥但聰明好學。據說是學校的四大校花之一,追求她的人從旺角排到了銅鑼灣,不過至今還單身。
阿珍探出頭看了看老徐的螢幕,全部都是綠色的曲線圖,好奇地問:“老徐,你看的這是什麼?”老徐看的太入神,轉過頭才發現阿珍,尬尷地笑了笑說:“我就是看看最近的行情。”老徐立馬切換了視窗。
阿珍沒在意又繼續問到:“Runnable
和Callable
兩個介面我總搞混,這個到底有什麼不同?”
面對阿珍的靈魂拷問,老徐淡定自若地說:“Runnable
是用於提供多執行緒任務支援的核心介面,Callable
是在Java 1.5中新增的Runnable
的改進版本。”
“在聊它們不同之前,我們先分別瞭解一下兩個介面。”老徐一邊說著,一邊開啟了原始碼:
Runnable介面
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Runnable
介面是一個函式式介面,它只有一個run()方法,不接受任何引數,也不返回任何值。由於方法簽名沒有指定throws
子句,因此無法進一步傳播已檢查的異常。它適用於我們不使用執行緒執行結果的情況,例如,非同步列印日誌:
package one.more;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingTask implements Runnable {
private static Logger logger = LoggerFactory.getLogger(LoggingTask.class);
private String name;
public LoggingTask(String name) {
this.name = name;
}
@Override
public void run() {
logger.info("{}說:你好!", this.name);
}
}
在上面例中,根據name
引數把資訊記錄在日誌檔案中,沒有返回值。我們可以通過Thread
啟動,比如:
public static void main(String[] args) {
String name = "萬貓學社";
Thread thread = new Thread(new LoggingTask(name));
thread.start();;
}
我們也可以通過ExecutorService
啟動,比如:
public static void main(String[] args) {
String name = "萬貓學社";
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new LoggingTask(name));
executorService.shutdown();
}
Callable介面
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Callable介面也是一個函式式介面,它只有一個call()方法,不接受任何引數,返回一個泛型值V,在方法簽名上包含throws Exception
子句,因此我們可以很容易地進一步傳播已檢查異常。它適用於我們使用執行緒執行結果的情況,例如,非同步計算階乘:
package one.more;
import java.util.concurrent.Callable;
public class FactorialTask implements Callable<Integer> {
private int n;
public FactorialTask(int n) {
this.n = n;
}
@Override
public Integer call() throws IllegalArgumentException {
int fact = 1;
if (n < 0) {
throw new IllegalArgumentException("必須大於等於零");
}
for (int i = n; i > 1; i--) {
fact = fact * i;
}
return fact;
}
}
在上面例中,根據n
引數計算它的階乘,並可以返回計算結結果。我們只能通過ExecutorService
啟動,比如:
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(new FactorialTask(5));
System.out.println(future.get());
executorService.shutdown();
}
call()方法的結果可以通過Future物件獲取到,如果在呼叫Future物件的get()方法時,call()方法出現了異常,異常會被繼續傳遞,比如:
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> future = executorService.submit(new FactorialTask(-1));
System.out.println(future.get());
executorService.shutdown();
}
丟擲如下異常:
老徐回頭看看了阿珍,說:“這回你知道有什麼不同了吧!”阿珍一頭霧水地說:“資訊量有點大呀,可以給我總結一下嗎?”“當然可以。”老徐回答。
總結
Runnable和Callable的不同:
- Callable的任務執行後可返回值,Runnable的任務不能返回值。
- Callable只可以通過
ExecutorService
啟動,Runnable可以通過Thread
和ExecutorService
啟動。 - Callable的call()方法可以傳播已檢查異常,Runnable的run()方法不可以。
最後,謝謝你這麼帥,還給我點贊和關注。
微信公眾號:萬貓學社
微信掃描二維碼
關注後回覆「電子書」
獲取12本Java必讀技術書籍