Table of Contents
ToggleDifference Between Runnable and Callable in Java
兩種接口都是多執行緒實現多任務可以同時執行的方式,但在實作上卻有一些不一樣的地方, Runnable 是一個接口,只定義了一個名為 run 的方法,並無傳回值,Callable 也是一個接口,定義了一個 泛型 V 傳回值的 call 方法,這兩者都是表示 Java 中由 Thread 執行的任務, Difference Between Callable and Runnable in Java 提供這兩種接口應用方式,本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- multithreading
| +- callable
| +- DifferenceRunnableCallableTest.java
單元測試
Difference Between Runnable and Callable in Java 提供執行任務、傳回值、拋出例外等操作。
runner
Difference Between Runnable and Callable 實作 Runnable ,建立 1 條執行緒,執行 1 個任務,每個任務耗時 3 秒完成,沒有任何傳回值。
@Test
public void runner() {
Thread thread = new Thread(() -> {
try {
System.out.println("T[" + Thread.currentThread().getId() + "] runner: ready");
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] runner: finished");
} catch (Exception ex) {
ex.printStackTrace();
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
T[11] runner: ready
T[11] runner: finished
caller
Difference Between Runnable and Callable 實作 Callable 建立 1 條執行緒,執行 1 個任務,每個任務耗時 3 秒完成,主執行緒會無限等待取得每個任務結束後的傳回值。
protected class Caller implements Callable<String> {
private String result;
@Override
public String call() throws Exception {
System.out.println("T[" + Thread.currentThread().getId() + "] caller: ready");
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] caller: finished");
result = "OK";
return result;
}
}
@Test
public void caller() {
List<FutureTask<String>> futureTasks = new ArrayList<FutureTask<String>>();
FutureTask<String> futureTask = new FutureTask<String>(new Caller());
futureTasks.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
futureTasks.forEach(e -> {
try {
String result = e.get();
System.out.println("T[" + Thread.currentThread().getId() + "] caller: " + result);
assertNotNull(result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
T[11] caller: ready
T[11] caller: finished
T[1] caller: OK
brokenCaller
Difference Between Runnable and Callable 實作 Callable 建立 1 條執行緒,執行 1 個任務,每個任務耗時 3 秒完成,執行緒拋出例外,主執行緒結束任務。
protected class BrokenCaller implements Callable<String> {
private String result;
@Override
public String call() throws Exception {
System.out.println("T[" + Thread.currentThread().getId() + "] caller: ready");
boolean flag = true;
if (flag) {
throw new IOException();
}
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] caller: finished");
result = "OK";
return result;
}
}
@Test
public void brokenCaller() {
List<FutureTask<String>> futureTasks = new ArrayList<FutureTask<String>>();
FutureTask<String> futureTask = new FutureTask<String>(new BrokenCaller());
futureTasks.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
futureTasks.forEach(e -> {
try {
String result = e.get();
System.out.println("T[" + Thread.currentThread().getId() + "] caller: " + result);
assertNotNull(result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
T[11] caller: ready
java.util.concurrent.ExecutionException: java.io.IOException
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.ruoxue.java_147.multithreading.DifferenceRunnableCallableTest.lambda$2(DifferenceRunnableCallableTest.java:98)
at java.util.ArrayList.forEach(ArrayList.java:1257)
Caused by: java.io.IOException
at org.ruoxue.java_147.multithreading.DifferenceRunnableCallableTest$BrokenCaller.call(DifferenceRunnableCallableTest.java:79)
at org.ruoxue.java_147.multithreading.DifferenceRunnableCallableTest$BrokenCaller.call(DifferenceRunnableCallableTest.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
DifferenceRunnableCallableTest.java
Difference Between Callable and Runnable 新增單元測試,驗證是否符合預期。
package org.ruoxue.java_147.multithreading.callable;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class DifferenceRunnableCallableTest {
@Test
public void runner() {
Thread thread = new Thread(() -> {
try {
System.out.println("T[" + Thread.currentThread().getId() + "] runner: ready");
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] runner: finished");
} catch (Exception ex) {
ex.printStackTrace();
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
protected class Caller implements Callable<String> {
private String result;
@Override
public String call() throws Exception {
System.out.println("T[" + Thread.currentThread().getId() + "] caller: ready");
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] caller: finished");
result = "OK";
return result;
}
}
@Test
public void caller() {
List<FutureTask<String>> futureTasks = new ArrayList<FutureTask<String>>();
FutureTask<String> futureTask = new FutureTask<String>(new Caller());
futureTasks.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
futureTasks.forEach(e -> {
try {
String result = e.get();
System.out.println("T[" + Thread.currentThread().getId() + "] caller: " + result);
assertNotNull(result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
protected class BrokenCaller implements Callable<String> {
private String result;
@Override
public String call() throws Exception {
System.out.println("T[" + Thread.currentThread().getId() + "] caller: ready");
boolean flag = true;
if (flag) {
throw new IOException();
}
TimeUnit.SECONDS.sleep(3);
System.out.println("T[" + Thread.currentThread().getId() + "] caller: finished");
result = "OK";
return result;
}
}
@Test
public void brokenCaller() {
List<FutureTask<String>> futureTasks = new ArrayList<FutureTask<String>>();
FutureTask<String> futureTask = new FutureTask<String>(new BrokenCaller());
futureTasks.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
futureTasks.forEach(e -> {
try {
String result = e.get();
System.out.println("T[" + Thread.currentThread().getId() + "] caller: " + result);
assertNotNull(result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
}
}
心得分享
Difference Between Runnable and Callable Interface in Java 的區別:
Callable Interface | Runnable Interface |
---|---|
宣告的方法是 call | 宣告的方法是 run |
在執行後可以傳回值 | 在執行後不能傳回值 |
call 方法可以拋出檢查及執行階段例外 | 無法拋出檢查例外,但可以拋出執行階段例外 |
會傳回 Future 物件,保存非同步計算的結果 | 不會傳回任何物件,無法保存結果 |