Java中的Runnable、Callable、Future、FutureTask的区别与示例

简介: Java中的Runnable、Callable、Future、FutureTask的区别与示例

Java中存在Runnable、Callable、Future、FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别。


Runnable


其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中,该函数没有返回值。然后使用某个线程去执行该runnable即可实现多线程,Thread类在调用start()函数后就是执行的是Runnable的run()函数。Runnable的声明如下

public interface Runnable {
    public abstract void run();
}


Callable


Callable与Runnable的功能大致相似,Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。Callable的声明如下 :

public interface Callable<V> {
    V call() throws Exception;
}


可以看到,这是一个泛型接口,call()函数返回的类型就是客户程序传递进来的V类型。 Executor就是Runnable和Callable的调度容器,Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果( Future简介 )。Future声明如下 :

public interface Future<T>
{
    V get() throws ...;
    V get(long timeout, TimeUnit unit) throws ...;
    void cancle(boolean mayInterrupt);
    boolean isCancelled();
    boolean isDone();
}

具体的实现类为java.util.concurrent.FutureTask<V>。


FutureTask


FutureTask则是一个RunnableFuture<V>,而RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口,

public class FutureTask<V> implements RunnableFuture<V>

  RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}


另外它还可以包装Runnable和Callable<V>, 由构造函数注入依赖。

public FutureTask(Callable<V> callable) {
    if (callable == null)
      throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;    // ensure visibility of callable
  }
  public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;    // ensure visibility of callable
  }

可以看到,Runnable注入会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。该适配函数的实现如下 :

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }


RunnableAdapter适配器

static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
      this.task = task;
      this.result = result;
    }
    public T call() {
      task.run();
      return result;
    }
  }

     由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。并且还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。


简单示例


package com.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
/**
 * Runnable和Callable的区别
 * Runnable没有返回值
 * Callable有返回值,值是通过Future获得的。
 * 
 * FutureTask是一个RunnableFuture<V>,而RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口,
  可以包装Runnable和Callable<V>, 由构造函数注入依赖。
 * @author hadoop
 *
 */
public class RunnableFutureTask {
    static  ExecutorService mExecutor = Executors.newSingleThreadExecutor();
    /**
     * 效率低下的斐波那契数列,耗时操作。这里没有引用外部共享域,fibc方法是线程安全的,不用考虑同步
     * @param num
     * @return
     */
    static int  fibc(int num){
      if(num == 0){
      return 0;
      }
      if(num == 1){
      return 1;
      }
      return  fibc(num-1)+fibc(num-2);
    }
    /**
     * Runnable,无返回值
     */
    static void runnableDemo(){
      new Thread(
         new Runnable() {
    @Override
    public void run() {
      System.out.println("Runnable demo:" + fibc(20));  
    }
    } 
      ).start();
    }
    /**
     * 其中Runnable实现的是void run()方法,无返回值;
     * Callable实现的是V call()方法,并且可以返回执行结果。
     * 其中Runnable可以提交给Thread来包装下,直接启动一个线程来执行,而Callable则一般都是提交给ExecutorService来执行
     */
    static void futureDemo(){
      try {
       /**
        * 提交Runnable则没有返回值,futurn没有数据
        * 使用submit提交任务会返回Future对象,而使用execute没有返回值。
        * submit提交Runnable任务本质上也是转化为Callable去执行
        */
    Future<?>  result = mExecutor.submit(new Runnable() {
    @Override
    public void run() {
       fibc(20);   
    }
    });
    System.out.println("future result from runnable:" + result.get());
    /**
    * 提交Callable,有返回值,future中能够获取返回值
    */
    Future<Integer> result2 = mExecutor.submit(new Callable<Integer>(){
    @Override
    public Integer call() throws Exception {
      // TODO Auto-generated method stub
      return fibc(20);
    }
    });
    System.out.println("future result from  callable:" + result2.get());
    /**
    * FutureTask是一个RunnableFuture<V>,而RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口
    * 同时包装了Runnable和Callable<V>, 由构造函数注入依赖。
    * Runnable注入会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务
    */
    FutureTask<Integer> futureTask = new FutureTask<Integer>(
      new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
        // TODO Auto-generated method stub
        return fibc(20);
        }
    });
    //提交futureTask
    mExecutor.submit(futureTask);
    System.out.println("future result from futureTask:" + futureTask.get());
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
            e.printStackTrace();
  }
    }
    public static void main(String[] args) {
    runnableDemo();
    futureDemo();
      System.out.println("已经开启所有的子线程");  
      mExecutor.shutdown();  
         System.out.println("shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。");  
         while(true){  
             if(mExecutor.isTerminated()){  
                 System.out.println("所有的子线程都结束了!");  
                 break;  
             }  
         }  
  }
}
目录
相关文章
|
17天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
2月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
46 1
|
4月前
|
并行计算 Java 大数据
Callable和Future
Callable和Future
|
4月前
|
存储 Java API
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
【Azure 存储服务】Java Storage SDK 调用 uploadWithResponse 代码示例(询问ChatGTP得代码原型后人力验证)
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
83 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。
|
2月前
|
存储 Java
什么是带有示例的 Java 中的交错数组?
什么是带有示例的 Java 中的交错数组?
48 9
|
2月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
27 1
|
2月前
|
Java API 网络安全
Java 发送邮件示例
本示例展示了如何使用Java编程语言发送电子邮件。通过利用JavaMail API,这段代码实现了从配置SMTP服务器,设置邮件属性,到发送邮件的全过程,为开发者提供了实用的参考。
|
3月前
|
JavaScript 前端开发 Java
Java 8 新特性详解及应用示例
Java 8 新特性详解及应用示例