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;  
             }  
         }  
  }
}
目录
相关文章
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
1824 1
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
3980 102
|
存储 缓存 人工智能
Java int和Integer的区别
本文介绍了Java中int与Integer的区别及==与equals的比较机制。Integer是int的包装类,支持null值。使用==比较时,int直接比较数值,而Integer比较对象地址;在-128至127范围内的Integer值可缓存,超出该范围或使用new创建时则返回不同对象。equals方法则始终比较实际数值。
477 0
|
安全 算法 Java
Java 中 synchronized 与 AtomicInteger 的区别
在Java多线程编程中,`synchronized`和`AtomicInteger`均用于实现线程安全,但原理与适用场景不同。`synchronized`是基于对象锁的同步机制,适用于复杂逻辑和多变量同步,如银行转账;而`AtomicInteger`采用CAS算法,适合单一变量的原子操作,例如计数器更新。二者各有优劣,应根据具体需求选择使用。
428 0
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
652 1
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
575 1
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
545 0
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
735 16