Callable接口及Futrue接口詳解


Callable接口

有兩種創建線程的方法-一種是通過創建Thread類,另一種是通過使用Runnable創建線程。但是,Runnable缺少的一項功能是,當線程終止時(即run()完成時),我們無法使線程返回結果。為了支持此功能,Java中提供了Callable接口。

  • 為了實現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種方法,但是由於下面的示例使用了庫中的具體實現,因此這里僅列出了重要的方法。

  • public boolean cancel(boolean mayInterrupt):用於停止任務。如果尚未啟動,它將停止任務。如果已啟動,則僅在mayInterrupt為true時才會中斷任務。
  • public Object get()拋出InterruptedException,ExecutionException:用於獲取任務的結果。如果任務完成,它將立即返回結果,否則將等待任務完成,然后返回結果。
  • public boolean isDone():如果任務完成,則返回true,否則返回false

可以看到Callable和Future做兩件事-Callable與Runnable類似,因為它封裝了要在另一個線程上運行的任務,而Future用於存儲從另一個線程獲得的結果。實際上,future也可以與Runnable一起使用。

要創建線程,需要Runnable。為了獲得結果,需要future。

Java庫具有具體的FutureTask類型,該類型實現Runnable和Future,並方便地將兩種功能組合在一起。
可以通過為其構造函數提供Callable來創建FutureTask。然后,將FutureTask對象提供給Thread的構造函數以創建Thread對象因此,間接地使用Callable創建線程。

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 + "毫秒】"; } }

輸出

----程序開始運行----
>>>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()); 
    } 
} 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM