Java Runnable Interface - Java 147

Java Runnable Interface – Java 147

Java 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 中的程式碼。

發佈留言