zoukankan      html  css  js  c++  java
  • 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()); 
        } 
    } 
     
  • 相关阅读:
    自定义View之实现流行的底部菜单栏中间突起:高仿“咸鱼APP”的底部菜单
    android获取内置和外置SD卡路径
    Android Environment.getExternalStorageDirectory() 获取的是内部存储还是外部存储?
    bitmap过大无法显示图片的问题
    Android Notification的使用
    Android Device Monitor 文件管理的常见问题
    BroadcastReceiver广播相关
    Android Studio 设置代码提示和代码自动补全快捷键--Eclipse 风格
    Android Studio com.android.support:percent 导入错误
    约束布局constraint-layout导入失败的解决方案
  • 原文地址:https://www.cnblogs.com/guanbin-529/p/11784914.html
Copyright © 2011-2022 走看看