Difference Between Runnable and Callable in Java - Java 147

Difference Between Runnable and Callable in Java – Java 147

Difference 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 InterfaceRunnable Interface
宣告的方法是 call宣告的方法是 run
在執行後可以傳回值在執行後不能傳回值
call 方法可以拋出檢查及執行階段例外無法拋出檢查例外,但可以拋出執行階段例外
會傳回 Future 物件,保存非同步計算的結果不會傳回任何物件,無法保存結果

發佈留言