创建线程的两种方式

  1. 直接继承 Thread
  2. 实现 Runnable 接口

这两种方式都有一个缺点:在执行完成任务之后,无法直接获取到最后的执行结果。如果需要获取执行结果,就必须通过共享变量线程通信的方式来达到想要的效果,较为麻烦。

所以从 Java 1.5 起,就提供了两种方式:CallableFuture,通过它们可以在任务执行结束后得到任务执行结果。

Runnable 与 Callable

首先是 java.lang.Rannable,它是一个接口,里面只声明了一个 run() 方法:

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

由于 run() 方法的返回值类型为 void,所以在线程执行完后无任何返回结果。

然后是 java.util.concurrent.Callable,它也是一个接口,也只声明了一个方法,其名为 call()

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

可以看到,这是一个泛型接口,返回的类型就是传进来的 V 类型。

那么,如何使用 Callable 呢?一般是结合 ExecutorService 来使用。ExecutorService 声明了几种 submit 方法,其中有一个就是传入 Callable

<T> Future<T> submit(Callable<T> task);

Future

Future 声明了对具体的 Runnable 或者 Callable 任务执行进行取消、查询、结果获取等方法。必要时可以通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果。

public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

由此可见,Future 提供了三种功能:

  1. 取消任务:参数表示是否允许中途取消(中断)
  2. 判断状态:是否已取消、是否已完成
  3. 获取结果:两种方式,指不指定时间

因为 Future 只是一个接口,所以无法直接用来创建对象,因此有了下面的 FutureTask

FutureTask

首先看下 FutureTask 的继承关系:

可以看出 RunnableFuture 继承了 Runnable 接口和 Future 接口,而 FutureTask 实现了 RunnableFuture 接口。所以它既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值

然后可以看到 FutureTask 内部有这几种状态:

private volatile int state;
private static final int NEW          = 0;
private static final int COMPLETING   = 1;
private static final int NORMAL       = 2;
private static final int EXCEPTIONAL  = 3;
private static final int CANCELLED    = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED  = 6;

再根据注释,可以得知当创建一个 FutureTask 对象时,初始状态是 NEW,在运行过程中,运行状态仅在方法setsetExceptioncancel 中转换为终端状态。有四种状态转换过程:

  • NEW -> COMPLETING -> NORMAL:正常执行并返回结果(run 执行成功再设置状态为 COMPLETING
  • NEW -> COMPLETING -> EXCEPTIONAL:执行过程中出现异常(setException 先设置状态为 COMPLETING
  • NEW -> CANCELLED:执行前被取消
  • NEW -> INTERRUPTING -> INTERRUPTED:执行时被中断(cancel 参数为 true 才可能出现这个状态)

最后来看下 FutureTask 的两个构造器:

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
}

可知传入的任务最后都是付给内部的 private Callable<V> callable

事实上,FutureTaskFuture 接口的一个唯一实现类。

更多关于 FutureTask 的源码分析,可以看 FutureTask源码解析 这篇文章。

使用示例

Future

第一种方式是使用继承了 ExecutorService 的线程池 ThreadPoolExecutor 中的 submit 方法,将 Callable 直接提交创建 Future

import java.util.concurrent.*;
public class FutureExample {static class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("do something in callable");Thread.sleep(5000);return "Ok";}}public static void main(String[] args) throws InterruptedException, ExecutionException {ExecutorService executorService = Executors.newCachedThreadPool();Future<String> future = executorService.submit(new MyCallable());System.out.println("do something in main");Thread.sleep(1000);String result = future.get();System.out.println("result: " + result);}
}

FutureTask

第二种方法是创建一个写好 CallableFutureTask 对象实例,再 submit。因为 FutureTask 内部本身拥有 run 方法,也可以直接创建线程 Thread 运行。

利用 ExecutorService

import java.util.concurrent.*
public class FutureTaskWithExecutorService {public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTask<String> futureTask = new FutureTask<>(() -> { // java 8 函数式写法System.out.println("do something in callable");Thread.sleep(5000);return "Ok";});ExecutorService executorService = Executors.newCachedThreadPool();executorService.submit(futureTask);System.out.println("do something in main");Thread.sleep(1000);String result = futureTask.get();System.out.println("result: " + result);}
}

利用 Thread

import java.util.concurrent.*
public class FutureTaskWithThread {public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("do something in callable");Thread.sleep(5000);return "Ok";}});new Thread(futureTask).start();System.out.println("do something in main");Thread.sleep(1000);String result = futureTask.get();System.out.println("result: " + result);}
}

参考资料

  1. Java并发编程:Callable、Future和FutureTask
  2. FutureTask源码解析

【Java并发】Runnable、Callable、Future、FutureTask相关推荐

  1. Java并发编程举例Runnable, Callable, Future, FutureTask, CompletionService

    import java.util.concurrent.*;/*** Created by chenh on 2017/3/23.*/ public class ConcurrentDemo {// ...

  2. Java并发编程笔记之FutureTask源码分析

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...

  3. JAVA并发编程学习笔记------FutureTask

    FutureTask是Future和Callable的结合体.传统的代码是这样写的 Future f = executor.submit(new Callable()); 然后通过Future来取得计 ...

  4. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础--线程安全性 Java 并发编程--Callable+Future+FutureTask java 并发编程--Thread 源码重新学习 java并发 ...

  5. Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable

    一.Exectuor框架简介 Java从1.5版本开始,为简化多线程并发编程,引入全新的并发编程包:java.util.concurrent及其并发编程框架(Executor框架). Executor ...

  6. java Future FutureTask 并发操作

    2019独角兽企业重金招聘Python工程师标准>>> 1.1 综述 创建线程有两种方式:extends Thread || inplements Runable,但是这两种方式都有 ...

  7. java并发编程之再学习

    java并发基础 Callable 首先我们来理一理Future和Runable的关系: public class FutureTask<V> implements RunnableFut ...

  8. 100道Java并发和多线程面试题

    1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓"知其然知其所以然","会用"只是 ...

  9. futuretask java 并发请求_【Java并发】Runnable、Callable、Future、FutureTask

    创建线程的两种方式 直接继承 Thread 实现 Runnable 接口 这两种方式都有一个缺点:在执行完成任务之后,无法直接获取到最后的执行结果.如果需要获取执行结果,就必须通过共享变量或线程通信的 ...

最新文章

  1. flask中jinjia2模板引擎详解3
  2. t oracle删除吗,Oracle 11g 手工建库与删库
  3. 路印智能钱包现已上线Google Play商店
  4. 2021-2025年中国丁基胶粘剂行业市场供需与战略研究报告
  5. 数据结构-队列,优先队列
  6. 破解防复制防刻录光盘
  7. Python如何实现简单DNF脚本
  8. Kubernetes-Host网络模式,指定Pod 物理机IP
  9. Ubuntu14.04安装GNOME3桌面
  10. 运行web项目提示异常:non-compatible bean definition of same name and class【com.xxx.xxx.XXX】
  11. 高铁盈利地图:东部赚翻 中西部普遍巨亏
  12. MAC IDEA启动后卡住不动
  13. libed2k源码导读:(二)Session接口以及实现分析
  14. Python金融科技:cufflinks绘制金融图表
  15. PyCrypto —— 一个极好的信息安全python库
  16. Java基础——LinkedList源码分析
  17. 每日一题(day5)
  18. MLP or IP:推荐模型到底用哪个更好?
  19. PG守护进程(Postmaster)——DefineCustomTypeVariable定义GUC参数
  20. 学习国际机票:航程的种类

热门文章

  1. 十大经典排序算法(动图演示)(转)
  2. 【淘宝技术这十年】,读后总结篇 转载
  3. c++ const常量的实现机制(转载)2
  4. debian的甘特图工具
  5. God of War Ascension / 战神4, 再一次迎来新导演!
  6. Java 混淆那些事(五):ProGuard 其他的选项
  7. 在Windows*上编译Tensorflow教程
  8. iOS启动页广告XHLaunchAd
  9. 如何在Angular 2项目中使用Bootstrap css库
  10. spark Application report for application_1498032012194_0036 (state: FAILED)