Table of Contents
ToggleJava Callable Interface
同一時間執行一個以上的執行緒,提升整體處理效能,提供建立新執行緒,執行指定任務,實作 Callable Interface ,覆寫 call 方法,任務完成時使用 Future 儲存不同執行緒的結果,FutureTask 實作了 Runnable 和 Future 介面,追踪其他執行緒的進度結果, Callable and Future in Java 本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- multithreading
| +- callable
| +- CallableInterfaceTest.java
單元測試
Callable Interface Java 提供等候超時、傳回值等操作。
worker
Callable Interface Java 建立 3 條執行緒,執行 3 個任務,每個任務耗時 3 秒完成,主執行緒會無限等待取得每個任務結束後的傳回值。
protected class Worker implements Callable<Object> {
private int id;
private Object result;
public Worker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public Object call() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
TimeUnit.SECONDS.sleep(3);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
result = id + " OK";
return result;
}
}
@Test
public void worker() {
try {
int taskSize = 3;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
List<Future<Object>> futures = new ArrayList<Future<Object>>();
for (int i = 0; i < taskSize; i++) {
Future<Object> future = executor.submit(new Worker(i));
futures.add(future);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
futures.forEach(e -> {
try {
Object result = e.get();
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
2023/02/17 17:43:20 T[12] worker: 1 ready
2023/02/17 17:43:20 T[11] worker: 0 ready
2023/02/17 17:43:20 T[13] worker: 2 ready
2023/02/17 17:43:23 T[11] worker: 0 finished
2023/02/17 17:43:23 T[12] worker: 1 finished
2023/02/17 17:43:23 T[13] worker: 2 finished
2023/02/17 17:43:23 T[1] worker: 0 OK
2023/02/17 17:43:23 T[1] worker: 1 OK
2023/02/17 17:43:23 T[1] worker: 2 OK
timeoutWorker
Callable Interface Java 建立 3 條執行緒,執行 3 個任務,每個任務耗時 3 秒完成,主執行緒取得每個任務結束後的傳回值,若 2 秒後若取不到值, ExecutorService Java 則會拋出例外結束等待。
protected class TimeoutWorker implements Callable<Object> {
private int id;
private Object result;
public TimeoutWorker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public Object call() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
TimeUnit.SECONDS.sleep(3);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
result = id + " OK";
return result;
}
}
@Test
public void timeoutWorker() {
try {
int taskSize = 3;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
List<Future<Object>> futures = new ArrayList<Future<Object>>();
for (int i = 0; i < taskSize; i++) {
Future<Object> future = executor.submit(new TimeoutWorker(i));
futures.add(future);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
futures.forEach(e -> {
try {
Object result = e.get(2, TimeUnit.SECONDS);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
2023/02/08 09:17:31 T[13] worker: 2 ready
2023/02/08 09:17:31 T[11] worker: 0 ready
2023/02/08 09:17:31 T[12] worker: 1 ready
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
at org.ruoxue.java_147.multithreading.CallableInterfaceTest.lambda$0(CallableInterfaceTest.java:59)
at java.util.ArrayList.forEach(ArrayList.java:1259)
2023/02/08 09:17:35 T[13] worker: 2 finished
2023/02/08 09:17:35 T[12] worker: 1 finished
2023/02/08 09:17:35 T[11] worker: 0 finished
2023/02/08 09:17:35 T[1] worker: 1 OK
2023/02/08 09:17:35 T[1] worker: 2 OK
CallableInterfaceTest.java
Callable Future FutureTask 新增單元測試,驗證 ExecutorService Java 是否符合預期。
package org.ruoxue.java_147.multithreading.callable;
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.TimeUnit;
import org.junit.Test;
public class CallableInterfaceTest {
protected class Worker implements Callable<Object> {
private int id;
private Object result;
public Worker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public Object call() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
TimeUnit.SECONDS.sleep(3);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
result = id + " OK";
return result;
}
}
@Test
public void worker() {
try {
int taskSize = 3;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
List<Future<Object>> futures = new ArrayList<Future<Object>>();
for (int i = 0; i < taskSize; i++) {
Future<Object> future = executor.submit(new Worker(i));
futures.add(future);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
futures.forEach(e -> {
try {
Object result = e.get();
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
protected class TimeoutWorker implements Callable<Object> {
private int id;
private Object result;
public TimeoutWorker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public Object call() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " ready");
TimeUnit.SECONDS.sleep(3);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id + " finished");
result = id + " OK";
return result;
}
}
@Test
public void timeoutWorker() {
try {
int taskSize = 3;
ExecutorService executor = Executors.newFixedThreadPool(taskSize);
List<Future<Object>> futures = new ArrayList<Future<Object>>();
for (int i = 0; i < taskSize; i++) {
Future<Object> future = executor.submit(new TimeoutWorker(i));
futures.add(future);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
futures.forEach(e -> {
try {
Object result = e.get(2, TimeUnit.SECONDS);
System.out.println(
sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
心得分享
使用 ExecutorService Java ,提交實作 Callable Interface ,覆寫 call 方法,使用 Callable and Future in Java 能拋出執行階段的執行例外,如: RuntimeException,及拋出設計階段的檢查例外,像是: IOException ,還能取得不同執行緒的結果, Callable Interface Java 實作任務後,使用 ExecutorService 調用 submit 建立一個新執行緒,此緒會執行 call 中的程式碼,並可取得傳回值。