FutureTask是什么?

线程池的实现核心之一是FutureTask。在提交任务时,用户实现的Callable实例task会被包装为FutureTask实例ftask;提交后任务异步执行,无需用户关心;当用户需要时,再调用FutureTask#get()获取结果——或异常。

基本使用

方法中可能会调用到多个服务/方法,且这些服务/方法之间是互相独立的,不存在先后关系。在高并发场景下,如果执行比较耗时,可以考虑多线程异步的方式调用。

我们先模拟两个耗时服务

一个150ms,一个200ms

  1. public class UserApi {
  2. /** 查询用户基本信息,模拟耗时150ms */
  3. public String queryUserInfo(long userId) {
  4. String userInfo = "userInfo: " + userId;
  5. try {
  6. TimeUnit.MILLISECONDS.sleep(150L);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. return userInfo;
  11. }
  12. /** 查询用户地址,模拟耗时200ms */
  13. public String queryUserAddress(long userId) {
  14. String userAddress = "userAddress: " + userId;
  15. try {
  16. TimeUnit.MILLISECONDS.sleep(200L);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. return userAddress;
  21. }
  22. }

不使用FutureTask

  1. @Test
  2. public void testNotUseFutureTask() {
  3. UserApi userApi = new UserApi();
  4. long userId = 12;
  5. long startTime = System.currentTimeMillis();
  6. // 获取用户基本信息
  7. String userInfo = userApi.queryUserInfo(userId);
  8. // 获取用户地址
  9. String userAddress = userApi.queryUserAddress(userId);
  10. System.err.println("testNotUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
  11. }

执行几次,结果:

  1. testNotUseFutureTask 耗时:358
  2. testNotUseFutureTask 耗时:360

从结果中,可以看到,总耗时是大于queryUserInfoqueryUserAddress之和的。但这两个服务逻辑上并不存在先后关系,理论上最长耗时取决于最慢的那个,即queryUserAddress

使用FutureTask

下例使用了FutureTask,来异步调用queryUserInfoqueryUserAddress

  1. @Test
  2. public void testUseFutureTask() throws ExecutionException, InterruptedException {
  3. UserApi userApi = new UserApi();
  4. long userId = 12;
  5. long startTime = System.currentTimeMillis();
  6. Callable<String> userInfoCallable = new Callable<String>() {
  7. @Override
  8. public String call() throws Exception {
  9. return userApi.queryUserInfo(userId);
  10. }
  11. };
  12. Callable<String> userAddressCallable = new Callable<String>() {
  13. @Override
  14. public String call() throws Exception {
  15. return userApi.queryUserAddress(userId);
  16. }
  17. };
  18. FutureTask<String> userInfoFutureTask = new FutureTask<>(userInfoCallable);
  19. FutureTask<String> userAddressFutureTask = new FutureTask<>(userAddressCallable);
  20. new Thread(userInfoFutureTask).start();
  21. new Thread(userAddressFutureTask).start();
  22. String userInfo = userInfoFutureTask.get();
  23. String userAddress = userAddressFutureTask.get();
  24. System.err.println("testUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
  25. }

执行几次,结果:

  1. testUseFutureTask 耗时:239
  2. testUseFutureTask 耗时:237

很明显,总耗时大大减少了,这就验证了前面所说,总耗时取决于queryUserAddress的耗时。

实现一个简单的FutureTask

从前面使用FutureTask的代码中可以看到,一个FutureTask需要包含以下几点:

  1. 1、范型
  2. 2、构造函数,传入Callable
  3. 3、实现Runnable
  4. 4、有返回值
  • MyFutureTask代码如下:
  1. public class MyFutureTask<T> implements Runnable {
  2. private Callable<T> callable;
  3. private T result;
  4. private String state;
  5. public MyFutureTask(Callable<T> callable) {
  6. this.callable = callable;
  7. }
  8. @Override
  9. public void run() {
  10. state = "NEW";
  11. try {
  12. result = callable.call();
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. state = "DONE";
  17. synchronized (this) {
  18. this.notify();
  19. }
  20. }
  21. /** 获取调用结果 */
  22. public T get() throws InterruptedException {
  23. if ("DOEN".equals(state)) {
  24. return result;
  25. }
  26. synchronized (this) {
  27. this.wait();
  28. }
  29. return result;
  30. }
  31. }
  • 使用:
  1. @Test
  2. public void testMyUseFutureTask() throws InterruptedException {
  3. UserApi userApi = new UserApi();
  4. long userId = 12;
  5. long startTime = System.currentTimeMillis();
  6. Callable<String> userInfoCallable = new Callable<String>() {
  7. @Override
  8. public String call() throws Exception {
  9. return userApi.queryUserInfo(userId);
  10. }
  11. };
  12. Callable<String> userAddressCallable = new Callable<String>() {
  13. @Override
  14. public String call() throws Exception {
  15. return userApi.queryUserAddress(userId);
  16. }
  17. };
  18. // 不同点
  19. MyFutureTask<String> userInfoFutureTask = new MyFutureTask<>(userInfoCallable);
  20. MyFutureTask<String> userAddressFutureTask = new MyFutureTask<>(userAddressCallable);
  21. new Thread(userInfoFutureTask).start();
  22. new Thread(userAddressFutureTask).start();
  23. String userInfo = userInfoFutureTask.get();
  24. String userAddress = userAddressFutureTask.get();
  25. System.err.println("testMyUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
  26. }
  • 输出结果:
  1. testMyUseFutureTask 耗时:208
  2. testMyUseFutureTask 耗时:211

从结果中看到,预期与使用FutureTask的一致。至于使用我们自定义的MyFutureTask执行耗时为何会比FutureTask长,我猜测是我们自己写的未做更多的检查和判断。我们自己写的只是用来学习FutureTask

总结

不使用异步的方式时,queryUserAddressqueryUserInfo执行之后才会执行,两者相加的时间算入总调用耗时。如果使用了异步线程调用,由于queryUserAddress耗时长,这样在queryUserAddress执行结束前,queryUserInfo就执行结束了,这样queryUserInfo调用耗时就不计了。

FutureTask简单实战的更多相关文章

  1. Nancy简单实战之NancyMusicStore(六):写在最后

    前言 由于公司搬家后,住的地方离上班的地方远了N倍,以前是走路十多分钟就可以到公司的,上班时间也从9:00提早到8:30 现在每天上班都是先坐公交,然后再坐地铁,在这段路上比较浪费时间而且每天都是要6 ...

  2. 移动端web开发初探之Vuejs的简单实战

    这段时间在做的东西,是北邮人论坛APP的注册页.这个注册页是内嵌的网页,因为打算安卓和IOS平台同时使用.因此实际上就是在做移动端的web开发了. 在这过程中遇到了不少有意思的东西. DEMO的git ...

  3. Element ui结合springboot的简单实战

    Eelment UI简单实战 前端开发 1 创建项目,导入element ui(略) 2 大致设计出想要的效果,如下 3 创建包 根据设计的大致模样在项目的components中创建对应的包,方便以后 ...

  4. Nancy简单实战之NancyMusicStore(一):准备工作和搭建项目

    开发环境 OS : Windows 10 10.0.14393 IDE : Visual Studio 2015 Community With Update 3 Database : PostgreS ...

  5. Nancy简单实战之NancyMusicStore(三):完善商品信息与管理

    前言 上一篇,我们做了不少准备,并且还把我们NancyFx音乐商城的首页打造好了.这一篇主要是完善我们在首页的商品浏览问题和添加对商品的管理. 下面开始正题: 商品详情 首先是查看单个商品的详情: 先 ...

  6. GUI简单实战——贪吃蛇

    将前面学到的GUI基础知识完成实战,完成一个简单的贪吃蛇项目 项目功能 用键盘上下左右实现贪吃蛇的自动移动 贪吃蛇吃到食物后,长度加一,分数加一 贪吃蛇吃到自己的身体,则游戏结束 按空格键实现游戏的暂 ...

  7. Nancy简单实战之NancyMusicStore(二):打造首页

    前言 继上一篇搭建好项目之后,我们在这一篇中将把我们NancyMusicStore的首页打造出来. 布局 开始首页之前,我们要先为我们的整个应用添加一个通用的布局页面,WebForm中母版页的概念. ...

  8. Nancy简单实战之NancyMusicStore(四):实现购物车

    前言 上一篇,我们完成了商品的详情和商品的管理,这一篇我们来完成最后的一个购物车功能. 购物车,不外乎这几个功能:添加商品到购物车,删除购物车中的商品,对购物车中的商品进行结算. MVC MusicS ...

  9. gradle教程 [原创](eclipse/ADT下 非插件 非Android Studio/AS)纯手打 第二篇:gradle简单实战

    一个bug 一个脚印的叫你们用gradle. 1介于网络上的很多资料都是老的 不适用与现在的新版本gradle 尤其是有些gradle方法改名了老的用不了 2介于网上都是粘贴复制并且零碎我很蛋疼啊,走 ...

随机推荐

  1. 分块 (貌似能用LCT做,反正我现在还不会) BZOJ 2002

    2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 9844  Solved: 5070[Submi ...

  2. 对WebView进行的一些设置

    webView.getSettings().setJavaScriptEnabled(true); //使用setting WebSettings webSettings = webView.getS ...

  3. ItemCF_基于物品的协同过滤

    ItemCF_基于物品的协同过滤 1.    概念 2.    原理 如何给用户推荐? 给用户推荐他没有买过的物品--103 3.    java代码实现思路 数据集: 第一步:构建物品的同现矩阵 第 ...

  4. javascript中各类的prototype属性

    prototype 作用:获取调用对象的对象原型引用 应用:可以为某对象原型添加方法 例: function getMax() { var max = this[0]; for(var x=0; x& ...

  5. laravel artisan 工具心得

    介绍一些非常好用的命令: 1.创建一个Eloquent模型:顺便创建一个对应的数据库表 php artisan make:model --migration Models/Admin/test 2.将 ...

  6. Java 中的几种线程池这么用才是对的

    为什么要使用线程池 虽然大家应该都已经很清楚了,但还是说一下.其实归根结底最主要的一个原因就是为了提高性能. 线程池和数据库连接池是同样的道理,数据库连接池是为了减少连接建立和释放带来的性能开销.而线 ...

  7. DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead extract-text-webpack-plugin 提取css报错

    深入浅出Webpack 1-5 使用pulugin extract-text-webpack-plugin 提取css报错 DeprecationWarning: Tapable.plugin is ...

  8. King's Quest POJ - 1904 匈牙利算法的思想+tarjan缩点+染色

    题目链接:https://cn.vjudge.net/problem/POJ-1904 自己一开始的想法,打算用匈牙利算法实现,找二分图的最大匹配.但是打了打发现,不太好实现.原因如下:匈牙利算法是不 ...

  9. 使用wifite破解路由器密码

    使用wifite破解路由器密码 发表于 2016-02-06   |   分类于 wifite  |   暂无评论  |   10次阅读 简介 wifite是一款自动化wep.wpa破解工具,不支持w ...

  10. SQLite3数据库的操作

    数据库的操作 我们在这个项目中使用的是SQLITE3数据库软件. 通过使用SQLITE3进行创建数据库,创建表,插入记录,查询记录,更新记录,关闭数据库等操作来实现将相应的数据存入数据库中. 打开数据 ...