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介于网上都是粘贴复制并且零碎我很蛋疼啊,走 ...
随机推荐
- HDU 4946 凸包
给你n个点,具有速度,一个位置如果有其他点能够先到,则不能继续访问,求出里面这些点哪些点是能够无限移动的. 首先我们考虑到,一个速度小的和一个速度大的,速度小的必定只有固定他周围的一定区域是它先到的, ...
- Tomcat处理一个http请求的过程
假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp 1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Con ...
- Hadoop 面试总结
1.简要描述如何安装配置一个开源的hadoop,只描述即可,列出完整步骤. a.创建一个用户和用户组,用来管理hadoop项目 b.修改确定ip地址:vim /etc/sysconfig/networ ...
- 【BZOJ】2049: [Sdoi2008]Cave 洞穴勘测 LCT
[题意]给定n个点和m个操作,每次操作:1.连接2个点.2.断开2个点.3.查询2个点是否连通.m<=2*10^5. [算法]Link-Cut Tree [题解]LCT模板题,Link,Cut, ...
- 【leetcode 简单】第三十六题 最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中. pop() -- 删除栈顶的元素. top() -- 获取栈顶元素. ...
- Count on a tree(SPOJ COT + 树上第k大 + 主席树 + LCA)
题目链接:https://www.spoj.com/problems/COT/en/ 题目: 题意: 给你一棵有n个节点的树,求节点u到节点v这条链上的第k大. 思路: 我们首先用dfs进行建题目给的 ...
- lintcode 40. 用栈实现队列
使用两个栈来回倒腾可以实现队列. AC代码: import java.util.Stack; public class Queue { private Stack<Integer> sta ...
- LOW逼三人组(一)----冒泡算法
排序 1.冒泡排序 冒泡算法 import random # 随机模块 def bubble_sort(li): ###################################冒泡排序#### ...
- JavaScript 使用闭包保护变量 防止污染
使用JavaScript编写插件或团队协作时,可使用闭包来解决此类以下两个问题: 1.定义过多全局变量,可能会造成全局变量命名冲突: 2.在插件内定义变量,需要保护该变量不被轻易修改: 优点:可以把局 ...
- gunicorn之日志详细配置
gunicorn的日志配置 gunicorn的日志配置相关的常用参数有4个,分别是accesslog,access_log_format,errorlog,loglevel. accesslog:用户 ...