Callable、Furture、FurtureTask

211 阅读3分钟
  • Callable、Furture、FurtureTask

    • 示意图:

      image-20220223004627876

    • Callable

      • 引入场景:使用Runnable创建线程,但是需要返回值

        • Runnable源码:内部的run是没有返回值的

           @FunctionalInterface
           public interface Runnable {
           ​
               public abstract void run();
           }
          
      • 概述:接口方法带有返回值(传入的泛型)

        • 本质上来说还是一个接口,但是这个里面

           @FunctionalInterface
           public interface Callable<V> {
            
               V call() throws Exception;
           }
          
      • 线程源码(Thread.java):并不是所有的方法都支持Callable参数(传进去可能也用不了,构造不支持)

        image-20220222232046164

        • 这个时候就引入了FurtureTask
    • FurtureTask:既可以作为Runnable交给线程执行;又可以作为Furture拿到Callable的结果

      • 概述:

        • 在JDK1.6之前,是有AQS进行实现的;1.7后完全实现了一套,但是还是基于AQS
        • 既实现了Runnable接口,又实现了Furture接口(获得处理结果),还可以拿到Callable的返回值;
      • 使得Callable可以被线程执行,无视Thread类中不能接受Callable类型参数的问题:

        • 实现了RunnableFurture

           public class FutureTask<V> implements RunnableFuture<V> 
          
          • RunnableFuture(这是一个接口):扩展自Runnable接口(意味着这个FutureTask是可以交给线程来执行的,这个东西就可以当成一个Runnable进行使用)

             public interface RunnableFuture<V> extends Runnable, Future<V> {
                 void run();
             }
            
        • FurtureTask类持有了Callable

           private Callable<V> callable;
          
          • 查看FurtureTask构造函数:可以接受一个Callable类型的实例,包装成一个FurtureTask,交给线程来执行

             public FutureTask(Callable<V> callable) {
                 if (callable == null)
                     throw new NullPointerException();
                 this.callable = callable;
                 this.state = NEW;       // ensure visibility of callable
             }
            
      • 概述

        • RunnableFuture扩展自Furture接口

          • Furture:JDK定义的,可以获得自定义执行任务的结果;还可以取消这个任务的执行

            • 就是将FurtureTask交给线程去执行的时候,想要拿到这个任务的执行结果,这个时候可以通过FurtureTask去拿到结果
            • 示意图:

              image-20220222234828354

    • 具体使用:使用FurtureTask配合Callable进行计算(需要拿到返回值并且添加随机中断)

      • 实现流程:

        1. 自定义业务类,实现Callable接口(这个东西就可以拿到线程执行结果的返回值,但是它不能适配所有的Thread方法;但是FurtureTask就可以,因为这家伙实现了Runnable接口,同时Thread内部有个构造方法,可以接收Runnable的实例化对象,此时就相当于转成了Thread的对象)

          • 需要添加中断检查机制
        2. 主线程中:

          1. 实例化实现了Callable接口的类,拿到对象

          2. 将对象传给FurtureTask,进行包装(Callable就可以交给线程执行了)

          3. 将FurtureTask传给Thread

          4. 采用随机中断机制

            • 在具体中断中调用FurtureTask的cancel方法并传入true
      • 实现细节:

        • FurtureTask调用中断的时候,不一定会执行(需要在线程中添加对应的逻辑)

          • FurtureTask调用中断

             futureTask.cancel(true);
            
          • 在线程中检查中断

               if(Thread.currentThread().isInterrupted()){
                System.out.println("Callable子线程任务被中断");
                return null;
             }
            
      • 完整代码:

         package cn.enjoyedu.ch2.future;
         ​
         import java.util.Random;
         import java.util.concurrent.Callable;
         import java.util.concurrent.ExecutionException;
         import java.util.concurrent.FutureTask;
         ​
         ​
         /**
          *类说明:演示Future等的使用
          */
         public class UseFuture {
            
            
            /*实现Callable接口,允许有返回值*/
            private static class UseCallable implements Callable<Integer>{
               private int sum;
               @Override
               public Integer call() throws Exception {
                  System.out.println("Callable子线程开始计算!");  
         //       Thread.sleep(2000);
                    for(int i=0 ;i<5000;i++){
                        if(Thread.currentThread().isInterrupted()){
                        System.out.println("Callable子线程任务被中断");
                        return null;
                     }
                       sum=sum+i;
                    }  
                    System.out.println("Callable子线程计算结束!结果为: "+sum);  
                    return sum; 
               }
            }
            
            public static void main(String[] args) 
                  throws InterruptedException, ExecutionException {
               //获取Callable实例
               UseCallable useCallable = new UseCallable();
               //用FurtureTask进行包装,这样就可以交给线程执行了,需要将Callable作为泛型实例
               FutureTask<Integer> futureTask //用FutureTask包装Callable
                  = new FutureTask<>(useCallable);
               //将任务交给线程执行
               new Thread(futureTask).start();//交给Thread去运行
               Random r = new Random();
               //必须加上一个休眠时间,因为线程可能没有启动完成
               Thread.sleep(1000);
               //FurtureTask既可以去拿线程的执行结果,也可以去终止任务(要终止一个线程,需要在内部进行处理)
               //用随机的方式决定是获得结果还是终止任务
               if(r.nextBoolean()) {
                  System.out.println("Get UseCallable result = "+futureTask.get());
               }else {
                  System.out.println("中断计算。  ");
                  futureTask.cancel(true);
               }
            }
         }
        
      • 运行截图(主线程中的随机中断机制选择不中断):实在不放心,可以打印随机的值来看

        image-20220223001436935

      • 运行截图(主线程中的随机中断机制选择中断):实在不放心,可以打印随机的值来看

        image-20220223001535425

\