Callable接口及Futrue接口詳解

Callable接口

有兩種建立線程的方法-一種是經過建立Thread類,另外一種是經過使用Runnable建立線程。可是,Runnable缺乏的一項功能是,當線程終止時(即run()完成時),咱們沒法使線程返回結果。爲了支持此功能,Java中提供了Callable接口。java

  • 爲了實現Runnable,須要實現不返回任何內容的run()方法,而對於Callable,須要實如今完成時返回結果的call()方法。請注意,不能使用Callable建立線程,只能使用Runnable建立線程。
  • 另外一個區別是call()方法能夠引起異常,而run()則不能。
  • 爲實現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; 
    } 
} 

Futrue接口

當call()方法完成時,結果必須存儲在主線程已知的對象中,以便主線程能夠知道該線程返回的結果。爲此,可使用Future對象。將Future視爲保存結果的對象–它可能暫時不保存結果,但未來會保存(一旦Callable返回)。所以,Future基本上是主線程能夠跟蹤進度以及其餘線程的結果的一種方式。要實現此接口,必須重寫5種方法,可是因爲下面的示例使用了庫中的具體實現,所以這裏僅列出了重要的方法。dom

  • public boolean cancel(boolean mayInterrupt):用於中止任務。若是還沒有啓動,它將中止任務。若是已啓動,則僅在mayInterrupt爲true時纔會中斷任務。
  • public Object get()拋出InterruptedException,ExecutionException:用於獲取任務的結果。若是任務完成,它將當即返回結果,不然將等待任務完成,而後返回結果。
  • public boolean isDone():若是任務完成,則返回true,不然返回false

能夠看到Callable和Future作兩件事-Callable與Runnable相似,由於它封裝了要在另外一個線程上運行的任務,而Future用於存儲從另外一個線程得到的結果。實際上,future也能夠與Runnable一塊兒使用。ide

要建立線程,須要Runnable。爲了得到結果,須要future。函數

Java庫具備具體的FutureTask類型,該類型實現Runnable和Future,並方便地將兩種功能組合在一塊兒。
能夠經過爲其構造函數提供Callable來建立FutureTask。而後,將FutureTask對象提供給Thread的構造函數以建立Thread對象所以,間接地使用Callable建立線程。
this

1.使用Callable和Future的完整示例

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

 

2.使用Callable和FutureTask的完整示例

// 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對象,還能夠取消任務,檢查任務是否完成或嘗試獲取結果。線程

3.使用Runnable來獲取返回結果的實現

// 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()); 
    } 
} 
相關文章
相關標籤/搜索