FutureTask简单实战
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
从结果中,可以看到,总耗时是大于queryUserInfo
和queryUserAddress
之和的。但这两个服务逻辑上并不存在先后关系,理论上最长耗时取决于最慢的那个,即queryUserAddress
使用FutureTask
下例使用了FutureTask
,来异步调用queryUserInfo
和queryUserAddress
。
@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
。
总结
不使用异步的方式时,queryUserAddress
在queryUserInfo
执行之后才会执行,两者相加的时间算入总调用耗时。如果使用了异步线程调用,由于queryUserAddress
耗时长,这样在queryUserAddress
执行结束前,queryUserInfo
就执行结束了,这样queryUserInfo
调用耗时就不计了。
FutureTask简单实战的更多相关文章
- Nancy简单实战之NancyMusicStore(六):写在最后
前言 由于公司搬家后,住的地方离上班的地方远了N倍,以前是走路十多分钟就可以到公司的,上班时间也从9:00提早到8:30 现在每天上班都是先坐公交,然后再坐地铁,在这段路上比较浪费时间而且每天都是要6 ...
- 移动端web开发初探之Vuejs的简单实战
这段时间在做的东西,是北邮人论坛APP的注册页.这个注册页是内嵌的网页,因为打算安卓和IOS平台同时使用.因此实际上就是在做移动端的web开发了. 在这过程中遇到了不少有意思的东西. DEMO的git ...
- Element ui结合springboot的简单实战
Eelment UI简单实战 前端开发 1 创建项目,导入element ui(略) 2 大致设计出想要的效果,如下 3 创建包 根据设计的大致模样在项目的components中创建对应的包,方便以后 ...
- Nancy简单实战之NancyMusicStore(一):准备工作和搭建项目
开发环境 OS : Windows 10 10.0.14393 IDE : Visual Studio 2015 Community With Update 3 Database : PostgreS ...
- Nancy简单实战之NancyMusicStore(三):完善商品信息与管理
前言 上一篇,我们做了不少准备,并且还把我们NancyFx音乐商城的首页打造好了.这一篇主要是完善我们在首页的商品浏览问题和添加对商品的管理. 下面开始正题: 商品详情 首先是查看单个商品的详情: 先 ...
- GUI简单实战——贪吃蛇
将前面学到的GUI基础知识完成实战,完成一个简单的贪吃蛇项目 项目功能 用键盘上下左右实现贪吃蛇的自动移动 贪吃蛇吃到食物后,长度加一,分数加一 贪吃蛇吃到自己的身体,则游戏结束 按空格键实现游戏的暂 ...
- Nancy简单实战之NancyMusicStore(二):打造首页
前言 继上一篇搭建好项目之后,我们在这一篇中将把我们NancyMusicStore的首页打造出来. 布局 开始首页之前,我们要先为我们的整个应用添加一个通用的布局页面,WebForm中母版页的概念. ...
- Nancy简单实战之NancyMusicStore(四):实现购物车
前言 上一篇,我们完成了商品的详情和商品的管理,这一篇我们来完成最后的一个购物车功能. 购物车,不外乎这几个功能:添加商品到购物车,删除购物车中的商品,对购物车中的商品进行结算. MVC MusicS ...
- gradle教程 [原创](eclipse/ADT下 非插件 非Android Studio/AS)纯手打 第二篇:gradle简单实战
一个bug 一个脚印的叫你们用gradle. 1介于网络上的很多资料都是老的 不适用与现在的新版本gradle 尤其是有些gradle方法改名了老的用不了 2介于网上都是粘贴复制并且零碎我很蛋疼啊,走 ...
随机推荐
- 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 ...
- NOIP模拟赛13
期望得分:100+0+100=200 实际得分:100+5+100=205 T1 空间卡到30M.. n<=2.5*1e7 若x是整除区间[1,n]每个数的最小的数,那么对[1,n]每个数分解质 ...
- Atcoder #014 agc014_D 树形DP+nim变形
LINK 题意:两人在一颗树上做游戏,先手可以将树上一个节点染白,后手染黑,到最后时,所有与黑色相邻的白色同时变黑.如果还存在白色,先手胜,否则后手胜. 思路:首先不考虑树上,单独为链时,不管找规律也 ...
- 使用Forms Authentication
using System; using System.Web; using System.Web.Security; namespace AuthTest { public class Aut ...
- CodeForces - 1004B
Sonya decided to organize an exhibition of flowers. Since the girl likes only roses and lilies, she ...
- NYOJ 1073 最大值 (模拟)
题目链接 输入N个数,M次查询. 每次查询给出一个数x. 要求:每次查询输出前x个数中第i小的数.(i为第i次查询) 你可以假设M <= N,Xi <= Xi+1 <= Xi+2 & ...
- 天梯赛 L2-20 功夫传人 (深搜)
一门武功能否传承久远并被发扬光大,是要看缘分的.一般来说,师傅传授给徒弟的武功总要打个折扣,于是越往后传,弟子们的功夫就越弱-- 直到某一支的某一代突然出现一个天分特别高的弟子(或者是吃到了灵丹.挖到 ...
- 仿阿里云后台管理界面模板html源码——后台
链接:http://pan.baidu.com/s/1nuH2SPj 密码:ar8o
- linux——vi和vim的区别
vi 和vim 的区别 它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面. vim的这些优势主要体现在以下几个方面:1.多级撤消我们知道 ...
- arch优化开机
查看开机时间 systemd-analyze 具体开机时间 systemd-analyze blame 你可以systemctl --all | grep not-found 查看有哪些服务挂掉了.然 ...