人物背景:
老徐,男,本名徐福貴,從事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();
}
抛出如下異常:
總結
- Callable的任務執行後可傳回值,Runnable的任務不能傳回值。
- Callable隻可以通過
啟動,Runnable可以通過ExecutorService
和Thread
啟動。ExecutorService
- Callable的call()方法可以傳播已檢查異常,Runnable的run()方法不可以。