Table of Contents
ToggleJava Runnable Interface
一直以來多執行緒,都是 Java 的主要功能之一,提供建立新執行緒,執行指定任務,實作 Runnable Interface 或繼承 Thread ,覆寫 run 方法,多執行緒程式包含兩個或多個可以並發執行的部分,每個部分可以同時處理不同的任務,優化系統資源,提升執行效率, Runnable Interface Java 的 run 方法並沒有提供傳回值,因此實作改寫加上傳回值的判斷,本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- multithreading
| +- runnable
| +- RunnableInterfaceTest.java
單元測試
Runnable Interface Java 提供拋出例外、傳回值等操作。
worker
Interface Runnable Java 建立 3 條執行緒,執行 3 個任務,每個任務耗時 3 秒完成,主執行緒會無限等待取得每個任務結束後的傳回值。
protected class Worker implements Runnable {
private int id;
private volatile boolean done = false;
private Object result;
public Worker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public void run() {
try {
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 = "OK";
done = true;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
synchronized (this) {
notifyAll();
}
}
}
public synchronized Object get() throws InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
while (!done) {
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id
+ " waiting");
wait();
}
return result;
}
}
@Test
public void worker() {
try {
int taskSize = 3;
List<Worker> workers = new ArrayList<Worker>();
for (int i = 0; i < taskSize; i++) {
Worker worker = new Worker(i);
workers.add(worker);
Thread thread = new Thread(worker);
thread.start();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
workers.forEach(e -> {
try {
Object result = e.get();
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: "
+ e.getId() + " result " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
2023/02/11 12:21:37 T[11] worker: 0 ready
2023/02/11 12:21:37 T[12] worker: 1 ready
2023/02/11 12:21:37 T[13] worker: 2 ready
2023/02/11 12:21:37 T[1] worker: 0 waiting
2023/02/11 12:21:40 T[13] worker: 2 finished
2023/02/11 12:21:40 T[11] worker: 0 finished
2023/02/11 12:21:40 T[12] worker: 1 finished
2023/02/11 12:21:40 T[1] worker: 0 result OK
2023/02/11 12:21:40 T[1] worker: 1 result OK
2023/02/11 12:21:40 T[1] worker: 2 result OK
timeoutWorker
Interface Runnable Java 建立 3 條執行緒,執行 3 個任務,每個任務耗時 3 秒完成,主執行緒取得每個任務結束後的傳回值,若 2 秒後若取不到值,Java Runnable Example 則會拋出例外結束等待。
protected class TimeoutWorker implements Runnable {
private int id;
private volatile boolean done = false;
private Object result;
public TimeoutWorker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public void run() {
try {
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 = "OK";
done = true;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
synchronized (this) {
notifyAll();
}
}
}
public synchronized Object get(long timeout) throws InterruptedException, TimeoutException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
long timeoutRemaining = timeout;
long awaitStarted = System.currentTimeMillis();
while (!done && timeoutRemaining > 0) {
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id
+ " waiting");
wait(timeout);
timeoutRemaining -= System.currentTimeMillis() - awaitStarted;
if (Thread.interrupted())
throw new InterruptedException();
}
if (!done)
throw new TimeoutException("TimeoutWorker " + id + " timeout");
return result;
}
}
@Test
public void timeoutWorker() {
try {
int taskSize = 3;
List<TimeoutWorker> workers = new ArrayList<TimeoutWorker>();
for (int i = 0; i < taskSize; i++) {
TimeoutWorker worker = new TimeoutWorker(i);
workers.add(worker);
Thread thread = new Thread(worker);
thread.start();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
workers.forEach(e -> {
try {
Object result = e.get(2000);
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: "
+ e.getId() + " result " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
2023/02/11 12:25:13 T[11] worker: 0 ready
2023/02/11 12:25:13 T[12] worker: 1 ready
2023/02/11 12:25:13 T[13] worker: 2 ready
2023/02/11 12:25:13 T[1] worker: 0 waiting
java.util.concurrent.TimeoutException: TimeoutWorker 0 timeout
at org.ruoxue.java_147.multithreading.RunnableInterfaceTest$TimeoutWorker.get(RunnableInterfaceTest.java:136)
at org.ruoxue.java_147.multithreading.RunnableInterfaceTest.lambda$1(RunnableInterfaceTest.java:157)
at java.util.ArrayList.forEach(ArrayList.java:1257)
2023/02/11 12:25:15 T[1] worker: 1 waiting
2023/02/11 12:25:16 T[12] worker: 1 finished
2023/02/11 12:25:16 T[11] worker: 0 finished
2023/02/11 12:25:16 T[13] worker: 2 finished
2023/02/11 12:25:16 T[1] worker: 1 result OK
2023/02/11 12:25:16 T[1] worker: 2 result OK
RunnableInterfaceTest.java
Interface Runnable Java 新增單元測試,驗證 Java Runnable Example 是否符合預期。
package org.ruoxue.java_147.multithreading.runnable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Test;
public class RunnableInterfaceTest {
protected class Worker implements Runnable {
private int id;
private volatile boolean done = false;
private Object result;
public Worker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public void run() {
try {
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 = "OK";
done = true;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
synchronized (this) {
notifyAll();
}
}
}
public synchronized Object get() throws InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
while (!done) {
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id
+ " waiting");
wait();
}
return result;
}
}
@Test
public void worker() {
try {
int taskSize = 3;
List<Worker> workers = new ArrayList<Worker>();
for (int i = 0; i < taskSize; i++) {
Worker worker = new Worker(i);
workers.add(worker);
Thread thread = new Thread(worker);
thread.start();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
workers.forEach(e -> {
try {
Object result = e.get();
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: "
+ e.getId() + " result " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
protected class TimeoutWorker implements Runnable {
private int id;
private volatile boolean done = false;
private Object result;
public TimeoutWorker(int id) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public void run() {
try {
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 = "OK";
done = true;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
synchronized (this) {
notifyAll();
}
}
}
public synchronized Object get(long timeout) throws InterruptedException, TimeoutException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
long timeoutRemaining = timeout;
long awaitStarted = System.currentTimeMillis();
while (!done && timeoutRemaining > 0) {
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: " + id
+ " waiting");
wait(timeout);
timeoutRemaining -= System.currentTimeMillis() - awaitStarted;
if (Thread.interrupted())
throw new InterruptedException();
}
if (!done)
throw new TimeoutException("TimeoutWorker " + id + " timeout");
return result;
}
}
@Test
public void timeoutWorker() {
try {
int taskSize = 3;
List<TimeoutWorker> workers = new ArrayList<TimeoutWorker>();
for (int i = 0; i < taskSize; i++) {
TimeoutWorker worker = new TimeoutWorker(i);
workers.add(worker);
Thread thread = new Thread(worker);
thread.start();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
workers.forEach(e -> {
try {
Object result = e.get(2000);
System.out.println(sdf.format(new Date()) + " T[" + Thread.currentThread().getId() + "] worker: "
+ e.getId() + " result " + result);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
心得分享
Java Runnable Example 有兩種方法可以啟動一個新執行緒,實作 Runnable Interface 或繼承 Thread ,覆寫 run 方法,使用 Interface Runnable Java 只能拋出執行階段的執行例外,如: RuntimeException,不能拋出設計階段的檢查例外,像是: FileNotFoundException , Runnable Interface Java 實作任務後傳遞給 Thread,利用 Thread 有一個接受 Runnable 的建構子,將實現者傳給 Thread ,然後調用 start 建立一個新執行緒,此緒會執行 run 中的程式碼。