FutureTask是什么?

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

基本使用

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

我们先模拟两个耗时服务

一个150ms,一个200ms

public class UserApi {

    /** 查询用户基本信息,模拟耗时150ms */
public String queryUserInfo(long userId) {
String userInfo = "userInfo: " + userId; try {
TimeUnit.MILLISECONDS.sleep(150L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return userInfo;
} /** 查询用户地址,模拟耗时200ms */
public String queryUserAddress(long userId) {
String userAddress = "userAddress: " + userId; try {
TimeUnit.MILLISECONDS.sleep(200L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return userAddress;
}
}

不使用FutureTask

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

执行几次,结果:

testNotUseFutureTask 耗时:358
testNotUseFutureTask 耗时:360

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

使用FutureTask

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

@Test
public void testUseFutureTask() throws ExecutionException, InterruptedException {
UserApi userApi = new UserApi(); long userId = 12;
long startTime = System.currentTimeMillis(); Callable<String> userInfoCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return userApi.queryUserInfo(userId);
}
};
Callable<String> userAddressCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return userApi.queryUserAddress(userId);
}
};
FutureTask<String> userInfoFutureTask = new FutureTask<>(userInfoCallable);
FutureTask<String> userAddressFutureTask = new FutureTask<>(userAddressCallable); new Thread(userInfoFutureTask).start();
new Thread(userAddressFutureTask).start(); String userInfo = userInfoFutureTask.get();
String userAddress = userAddressFutureTask.get();
System.err.println("testUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
}

执行几次,结果:

testUseFutureTask 耗时:239
testUseFutureTask 耗时:237

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

实现一个简单的FutureTask

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

1、范型
2、构造函数,传入Callable
3、实现Runnable
4、有返回值
  • MyFutureTask代码如下:
public class MyFutureTask<T> implements Runnable {

    private Callable<T> callable;
private T result;
private String state; public MyFutureTask(Callable<T> callable) {
this.callable = callable;
} @Override
public void run() {
state = "NEW";
try {
result = callable.call();
} catch (Exception e) {
e.printStackTrace();
}
state = "DONE";
synchronized (this) {
this.notify();
}
} /** 获取调用结果 */
public T get() throws InterruptedException {
if ("DOEN".equals(state)) {
return result;
}
synchronized (this) {
this.wait();
}
return result;
}
}
  • 使用:
@Test
public void testMyUseFutureTask() throws InterruptedException {
UserApi userApi = new UserApi(); long userId = 12;
long startTime = System.currentTimeMillis(); Callable<String> userInfoCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return userApi.queryUserInfo(userId);
}
};
Callable<String> userAddressCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return userApi.queryUserAddress(userId);
}
}; // 不同点
MyFutureTask<String> userInfoFutureTask = new MyFutureTask<>(userInfoCallable);
MyFutureTask<String> userAddressFutureTask = new MyFutureTask<>(userAddressCallable); new Thread(userInfoFutureTask).start();
new Thread(userAddressFutureTask).start(); String userInfo = userInfoFutureTask.get();
String userAddress = userAddressFutureTask.get();
System.err.println("testMyUseFutureTask 耗时:" + (System.currentTimeMillis() - startTime));
}
  • 输出结果:
testMyUseFutureTask 耗时:208
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. jdbc:oracle:thin:@localhost:1521:orcl和jdbc:oracle:thin:@localhost:1521/orcl的区别

      Oracle Thin JDBC Driver 驱动程序包名:ojdbc14.jar.ojdbc6.jar 驱动程序类名: oracle.jdbc.driver.OracleDriver JDBC ...

  2. NOIP模拟赛13

    期望得分:100+0+100=200 实际得分:100+5+100=205 T1 空间卡到30M.. n<=2.5*1e7 若x是整除区间[1,n]每个数的最小的数,那么对[1,n]每个数分解质 ...

  3. Atcoder #014 agc014_D 树形DP+nim变形

    LINK 题意:两人在一颗树上做游戏,先手可以将树上一个节点染白,后手染黑,到最后时,所有与黑色相邻的白色同时变黑.如果还存在白色,先手胜,否则后手胜. 思路:首先不考虑树上,单独为链时,不管找规律也 ...

  4. 使用Forms Authentication

    using System; using System.Web; using System.Web.Security;   namespace AuthTest {   public class Aut ...

  5. CodeForces - 1004B

    Sonya decided to organize an exhibition of flowers. Since the girl likes only roses and lilies, she ...

  6. NYOJ 1073 最大值 (模拟)

    题目链接 输入N个数,M次查询. 每次查询给出一个数x. 要求:每次查询输出前x个数中第i小的数.(i为第i次查询) 你可以假设M <= N,Xi <= Xi+1 <= Xi+2 & ...

  7. 天梯赛 L2-20 功夫传人 (深搜)

    一门武功能否传承久远并被发扬光大,是要看缘分的.一般来说,师傅传授给徒弟的武功总要打个折扣,于是越往后传,弟子们的功夫就越弱-- 直到某一支的某一代突然出现一个天分特别高的弟子(或者是吃到了灵丹.挖到 ...

  8. 仿阿里云后台管理界面模板html源码——后台

    链接:http://pan.baidu.com/s/1nuH2SPj 密码:ar8o

  9. linux——vi和vim的区别

    vi 和vim 的区别   它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面. vim的这些优势主要体现在以下几个方面:1.多级撤消我们知道 ...

  10. arch优化开机

    查看开机时间 systemd-analyze 具体开机时间 systemd-analyze blame 你可以systemctl --all | grep not-found 查看有哪些服务挂掉了.然 ...