有兩種建立線程的方法-一種是經過建立Thread類,另外一種是經過使用Runnable建立線程。可是,Runnable缺乏的一項功能是,當線程終止時(即run()完成時),咱們沒法使線程返回結果。爲了支持此功能,Java中提供了Callable接口。java
爲實現Callable而必須重寫call方法。併發
// Java program to illustrate Callable // to return a random number import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class CallableExample implements Callable { public Object call() throws Exception { // Create random number generator Random generator = new Random(); Integer randomNumber = generator.nextInt(5); // To simulate a heavy computation, // we delay the thread for some random time Thread.sleep(randomNumber * 1000); return randomNumber; } }
當call()方法完成時,結果必須存儲在主線程已知的對象中,以便主線程能夠知道該線程返回的結果。爲此,可使用Future對象。將Future視爲保存結果的對象–它可能暫時不保存結果,但未來會保存(一旦Callable返回)。所以,Future基本上是主線程能夠跟蹤進度以及其餘線程的結果的一種方式。要實現此接口,必須重寫5種方法,可是因爲下面的示例使用了庫中的具體實現,所以這裏僅列出了重要的方法。dom
能夠看到Callable和Future作兩件事-Callable與Runnable相似,由於它封裝了要在另外一個線程上運行的任務,而Future用於存儲從另外一個線程得到的結果。實際上,future也能夠與Runnable一塊兒使用。ide
要建立線程,須要Runnable。爲了得到結果,須要future。函數
Java庫具備具體的FutureTask類型,該類型實現Runnable和Future,並方便地將兩種功能組合在一塊兒。
能夠經過爲其構造函數提供Callable來建立FutureTask。而後,將FutureTask對象提供給Thread的構造函數以建立Thread對象。所以,間接地使用Callable建立線程。
this
package com.example.thread.callable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.*; /** * @author: GuanBin * @date: Created in 下午11:19 2019/10/31 */ public class TestCallable implements Callable<Object> { private int taskNum; public TestCallable(int taskNum) { this.taskNum = taskNum; }
//1,2主要區別是建立線程的方式 public static void main(String[] args) throws ExecutionException, InterruptedException { test1(); test2(); } /** * 使用Executors.newFixedThreadPool建立線程池 * @throws InterruptedException * @throws ExecutionException */ private static void test1() throws InterruptedException, ExecutionException { System.out.println("----程序開始運行----"); Date date1 = new Date(); int taskSize=5; ExecutorService pool = Executors.newFixedThreadPool(taskSize); List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new TestCallable(i); // 執行任務並獲取Future對象 Future f = pool.submit(c); list.add(f); } // 關閉線程池 pool.shutdown(); // 獲取全部併發任務的運行結果 for (Future f : list) { // 從Future對象上獲取任務的返回值,並輸出到控制檯 System.out.println(">>>" + f.get().toString()); //OPTION + return 拋異常 } Date date2 = new Date(); System.out.println("----程序結束運行----,程序運行時間【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } /** * 線程直接使用new Thread來建立 * @throws ExecutionException * @throws InterruptedException */ private static void test2() throws ExecutionException, InterruptedException { System.out.println("----程序開始運行----"); Date date1 = new Date(); int taskSize=5; FutureTask[] randomNumberTasks = new FutureTask[5]; List<Future> list = new ArrayList<Future>(); for (int i = 0; i < randomNumberTasks.length; i++) { Callable c = new TestCallable(i); // 執行任務並獲取Future對象 randomNumberTasks[i]= new FutureTask(c); Thread t = new Thread(randomNumberTasks[i]); t.start(); } // 獲取全部併發任務的運行結果 for (Future f : randomNumberTasks) { // 從Future對象上獲取任務的返回值,並輸 System.out.println(">>>" + f.get().toString()); //OPTION + return 拋異常 } Date date2 = new Date(); System.out.println("----程序結束運行----,程序運行時間【" + (date2.getTime() - date1.getTime()) + "毫秒】"); } /** * call方法的實現,主要用於執行線程的具體實現,並返回結果 * @return * @throws Exception */ @Override public Object call() throws Exception { System.out.println(">>>" + taskNum + "任務啓動"); Date dateTmp1 = new Date(); Thread.sleep(1000); Date dateTmp2 = new Date(); long time = dateTmp2.getTime() - dateTmp1.getTime(); System.out.println(">>>" + taskNum + "任務終止"); return taskNum + "任務返回運行結果,當前任務時間【" + time + "毫秒】"; } }
輸出spa
----程序開始運行---- >>>0任務啓動 >>>1任務啓動 >>>2任務啓動 >>>3任務啓動 >>>4任務啓動 >>>0任務終止 >>>0任務返回運行結果,當前任務時間【1002毫秒】 >>>1任務終止 >>>2任務終止 >>>4任務終止 >>>1任務返回運行結果,當前任務時間【1005毫秒】 >>>2任務返回運行結果,當前任務時間【1005毫秒】 >>>3任務終止 >>>3任務返回運行結果,當前任務時間【1005毫秒】 >>>4任務返回運行結果,當前任務時間【1005毫秒】 ----程序結束運行----,程序運行時間【1007毫秒】 Process finished with exit code 0
// Java program to illustrate Callable and FutureTask // for random number generation import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class CallableExample implements Callable { public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class CallableFutureTest { public static void main(String[] args) throws Exception { // FutureTask is a concrete class that // implements both Runnable and Future FutureTask[] randomNumberTasks = new FutureTask[5]; for (int i = 0; i < 5; i++) { Callable callable = new CallableExample(); // Create the FutureTask with Callable randomNumberTasks[i] = new FutureTask(callable); // As it implements Runnable, create Thread // with FutureTask Thread t = new Thread(randomNumberTasks[i]); t.start(); } for (int i = 0; i < 5; i++) { // As it implements Future, we can call get() System.out.println(randomNumberTasks[i].get()); // This method blocks till the result is obtained // The get method can throw checked exceptions // like when it is interrupted. This is the reason // for adding the throws clause to main } } }
啓動線程後,與線程的全部交互都使用FutureTask,由於它實現了Future接口。所以,不須要存儲Thread對象。使用FutureTask對象,還能夠取消任務,檢查任務是否完成或嘗試獲取結果。線程
// Java program to illustrate Runnable // for random number generation import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class RunnableExample implements Runnable { // Shared object to store result private Object result = null; public void run() { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); // As run cannot throw any Exception try { Thread.sleep(randomNumber * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // Store the return value in result when done result = randomNumber; // Wake up threads blocked on the get() method synchronized(this) { notifyAll(); } } public synchronized Object get() throws InterruptedException { while (result == null) wait(); return result; } } // Code is almost same as the previous example with a // few changes made to use Runnable instead of Callable public class RunnableTest { public static void main(String[] args) throws Exception { RunnableExample[] randomNumberTasks = new RunnableExample[5]; for (int i = 0; i < 5; i++) { randomNumberTasks[i] = new RunnableExample(); Thread t = new Thread(randomNumberTasks[i]); t.start(); } for (int i = 0; i < 5; i++) System.out.println(randomNumberTasks[i].get()); } }