Java Runnable,Callable和FutureTask详解

目 录

[1 序言](#1 序言)

[2 基本概念](#2 基本概念)

[2.1 Runnable 和 Callable的区别](#2.1 Runnable 和 Callable的区别)

[2.2 Future 和 FutureTask](#2.2 Future 和 FutureTask)

[2.3 ExecutorService中Future的应用](#2.3 ExecutorService中Future的应用)

[2.4 Future submit(Runnable task)](#2.4 Future submit(Runnable task))

[2.5 Future submit(Callable task)](#2.5 Future submit(Callable task))

[2.6 ExecutorService中execute()在ThreadPoolExecutor中实现如下:](#2.6 ExecutorService中execute()在ThreadPoolExecutor中实现如下:)

[3 Future,RunnableFuture,FutureTask 关系](#3 Future,RunnableFuture,FutureTask 关系)

[4 总结](#4 总结)

参考文献


1 序言

本文针对多线程中使用的几种任务:Runnable、Callable、RunnableFuture,FutureTask等进行详细介绍

2 基本概念

2.1 Runnable 和 Callable的区别

Runnable和Callable都是定义了接口,可以用在线程池中异步执行,区别是:

  • Runnable可以直接被Thread执行,但是没有返回值
  • Callable执行之后有返回值,但是只能提交给线程池执行。

2.2 Future 和 FutureTask

Future是一个接口,主要是线程池中任务执行之后用于返回结果的获取,定义了

  • boolean cancel(boolean mayInterruptIfRunning); 取消任务
  • boolean isCancelled(); 任务是否取消
  • boolean isDone(); 任务是否执行完毕
  • V get(); 获取任务执行的结果,注意这个方法阻塞线程
  • V get(long timeout, TimeUnit unit); 同上,只是增加了一个超时时间

Future有一个直接继承接口RunnableFuture,RunnableFuture有一个实现的子类FutureTask,RunnableFuture这个接口同时还继承了Runnable接口,这意味着FutureTask可以作为Future或者Runnable使用。

再来看一下FutureTask的实现,最终内部保存了一个Callable对象,也就是提交的任务

先看构造函数

一共2个构造函数,一个是接受Callable,一个是接受Runnable和默认返回值。

详细看一下第二个构造参数,注释很清楚的说明,当你需要runnable可取消同时不关心返回值时,可以这样构建

上面两个函数将一个Runnable适配成了一个Callable,是Executors中提供的静态方法。

再看一下FutureTask对Runnable的实现

抛开其他的判断条件,其实就是对内部保存的Callable调用了call方法,进行执行并保存结果。这就是FutureTask主要的几个方法,下面有用。

2.3 ExecutorService中Future的应用

上面2点主要是为了给这点做伏笔,现在我们来看为什么ExecutorService中的submit()既可以提交Runnable又可以提交Callable并返回结果,同时看看直接execute() Runnable会有什么不同。

2.4 Future submit(Runnable task)

先来看一下这个方法的实现

代码上可以很直观的看到,提交的Runnable被newTaskFor()适配成了RunnableFuture。来看一下newTaskFor()这个方法的实现。

复制代码

直接是new了一个FutureTask对象,上面我们分析过这种情况,runnable其实是会被适配成一个Callable的。

2.5 Future submit(Callable task)

再来看一下这个方法

复制代码

跟上面的代码简直一摸一样,都是适配成了RunnableFuture。

看到这里可以明白,提交Runnable时是将Runnable适配成了Callable,也就是submit方法最终都会调用的的是Callable对象。

上面我们说过RunnableFuture实现了Runnable接口,当他被execute时,肯定是被当作Runnable使用的,看一下两个submit方法最终都是通过execute来执行的。

上面介绍FutureTask时我们知道,对Runnable的实现FutureTask最后调用的是Callable的call方法。

到这里可以知道了,

  1. 当我们提交一个Runnable的任务时,首先通过FutureTask的构造函数被适配成了一个Callable对象被保存FutureTask中。
  2. 当任务被执行时,FutureTask又被当作一个Runnable使用,调用了保存在内部的Callable的call方法,任务被执行并返回了结果。
  3. Runnable被适配成Callable时最终调用的还是自己的run方法。

2.6 ExecutorService中execute()在ThreadPoolExecutor中实现如下:

上面注释的意思是:

  1. 当前核心线程数少于corePoolSize是,尝试直接新建Thread用来执行任务。同时校验添加的过程,防止出错。
  2. 任务入队时二次校验是否需要新建线程,判断是否需要回滚等。
  3. 如果任务不能入队则新建非核心线程处理,如果失败那么就拒绝任务。

这个就是任务具体执行的过程

3 Future,RunnableFuture,FutureTask 关系

再来看构造方法,有两个构造方法如下

4 总结

主要讲了几个概念Runnable、Callable、Future,RunnableFuture,FutureTask 以及相关的子类,总结如下:

  • Runnable可以直接被Thread执行,但是没有返回值
  • Callable执行之后有返回值,但是只能提交给线程池执行。
  • Future定义了一系列关于任务管理的接口方法
  • FutureTask是Future唯一实现类,它也实现了Runnable接口
  • 线程池submit Callable和Runnable时最终都会转换成FutureTask
  • FutureTask被执行时是被当成Runnable使用的,执行了内部保存的Callable的call方法

参考文献

https://juejin.cn/post/6844903672736907272

https://blog.csdn.net/weixin_34244102/article/details/87973014

https://blog.csdn.net/sageyin/article/details/116035166

相关推荐
我是ed.4 分钟前
cocos Js 使用 webview 通过 postMessage 进行通信
开发语言·javascript·ecmascript
段ヤシ.9 分钟前
Windows环境下安装Python和PyCharm
开发语言·python·pycharm
大萌神Nagato14 分钟前
如何修改VM虚拟机中的ip
linux·开发语言·ip·虚拟机·静态ip
陈煜的博客36 分钟前
elasticSearch 增删改查 java api
java·大数据·elasticsearch
hweiyu0038 分钟前
Scala实用编程(附电子书资料)
开发语言·后端·scala
mftang42 分钟前
C 标准库 <time.h> 函数详解
c语言·开发语言
lly20240643 分钟前
SVG 在线编辑器
开发语言
桦说编程1 小时前
交替打印最容易理解的实现——同步队列
java·后端·设计模式
脑袋大大的1 小时前
uni-app x开发避坑指南:拯救被卡顿的UI线程!
开发语言·前端·javascript·vue.js·ui·uni-app·uts
土族程序员1 小时前
JavaFX CSS @font-face 错误全面分析 loadStylesheetUnPrivileged / reportException
java·css·javafx