Java Callable Interface - Java 147

Java Callable Interface – Java 147

Java 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 中的程式碼,並可取得傳回值。

發佈留言