3

Callable接口及Futrue接口详解

 2 years ago
source link: https://www.cnblogs.com/guanbin-529/p/11784914.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.



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()); 
    } 
} 

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK