(原创)Callable、FutureTask中阻塞超时返回的坑点
直接上代码
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private long waitTime;
public MyCallable(int timeInMillis){
this.waitTime=timeInMillis;
}
@Override
public String call() throws Exception {
Thread.sleep(waitTime);
return Thread.currentThread().getName();
}
}
结果阻塞的代码
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; public class FutureTaskExample { public static void main(String[] args) {
MyCallable callable1 = new MyCallable();
MyCallable callable2 = new MyCallable(); FutureTask<String> futureTask1 = new FutureTask<String>(callable1);
FutureTask<String> futureTask2 = new FutureTask<String>(callable2); ExecutorService executor = Executors.newFixedThreadPool();
executor.execute(futureTask1);
executor.execute(futureTask2); while (true)
{
try {
if(futureTask1.isDone() && futureTask2.isDone()){
System.out.println("Done");
//shut down executor service
executor.shutdown();
return;
} if(!futureTask1.isDone()){
//阻塞futureTask1
System.out.println("FutureTask1 output="+futureTask1.get());
} if(!futureTask2.isDone()){
//阻塞futureTask2
System.out.println("FutureTask2 output="+futureTask2.get());
} } catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}catch(Exception e){
//do nothing
}
} } }
运行结果很简单,必须是:
FutureTask1 output=pool-1-thread-1
FutureTask2 output=pool-1-thread-2
Done
如果改为阻塞超时,先猜猜输出结果是什么。注意第37行代码有超时处理。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; public class FutureTaskExample { public static void main(String[] args) {
MyCallable callable1 = new MyCallable();
MyCallable callable2 = new MyCallable(); FutureTask<String> futureTask1 = new FutureTask<String>(callable1);
FutureTask<String> futureTask2 = new FutureTask<String>(callable2); ExecutorService executor = Executors.newFixedThreadPool();
executor.execute(futureTask1);
executor.execute(futureTask2); while (true)
{
try {
if(futureTask1.isDone() && futureTask2.isDone()){
System.out.println("Done");
//shut down executor service
executor.shutdown();
return;
} if(!futureTask1.isDone()){
//阻塞futureTask1
System.out.println("FutureTask1 output="+futureTask1.get());
} System.out.println("Waiting for FutureTask2 to complete");
String s = futureTask2.get(500L, TimeUnit.MILLISECONDS); //阻塞500毫秒
if(s !=null){
System.out.println("FutureTask2 output="+s);
}
else{
System.out.println("FutureTask2 output is null");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}catch(Exception e){
//do nothing
}
} } }
如果说是这样的结果,那就错了
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to complete
FutureTask2 output is null
Waiting for FutureTask2 to complete
FutureTask2 output is null
FutureTask2 output=pool-1-thread-2
Done
最终输出
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
FutureTask2 output=pool-1-thread-2
Done
说明了一件事,即在超时期限内,如果未能获取线程返回值,futureTask2.get(500L, TimeUnit.MILLISECONDS) 将不对继续执行后面的代码,而是进行下一次的while操作了(并不是返回null),while的下一次循环,直到获取到了返回结果,String s才得以赋值,代码继续进行。
所以要慎用get(long timeout, TimeUnit unit)。
传统的理解是错误的:
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
大神 海子 曾对这个问题有质疑,认为会抛出异常,并赋空值,见:
http://www.cnblogs.com/dolphin0520/p/3949310.html#3318489
我尝试修改代码
String s="aa";
while (true)
{
try {
if(futureTask1.isDone() && futureTask2.isDone()){
System.out.println("Done");
//shut down executor service
executor.shutdown();
return;
} if(!futureTask1.isDone()){
//阻塞futureTask1
System.out.println("FutureTask1 output="+futureTask1.get());
} System.out.println("Waiting for FutureTask2 to complete");
s = futureTask2.get(500L, TimeUnit.MILLISECONDS); //阻塞500毫秒
if(s !=null){
System.out.println("FutureTask2 output="+s);
}
else{
System.out.println("FutureTask2 output is null");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}catch(Exception e){
System.out.println("s is:"+s);
//do nothing
}
}
s的预设值那里有改变:String s="aa";也没发现变为null,是没发生赋值。在异常中s也没有被赋空值。
所以在使用get(long timeout, TimeUnit unit)的时候,变量初始最好能给一个空值,这样就不会产生奇怪的结果,这也是合理的编程习惯。
(原创)Callable、FutureTask中阻塞超时返回的坑点的更多相关文章
- 【转载】Callable、FutureTask中阻塞超时返回的坑点
本文转载自:http://www.cnblogs.com/starcrm/p/5010863.html 案例1: package com.net.thread.future; import java. ...
- ExecutorService、Callable、Future实现有返回结果的多线程原理解析
原创/朱季谦 在并发多线程场景下,存在需要获取各线程的异步执行结果,这时,就可以通过ExecutorService线程池结合Callable.Future来实现. 我们先来写一个简单的例子-- pub ...
- java笔记--用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程
用ThreadLocal管理线程,Callable<V>接口实现有返回值的线程 ThreadLocal在我的笔记"关于线程同步"的第5种方式里面有介绍,这里就不多说了. ...
- 并发编程-Future+callable+FutureTask 闭锁机制
项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable实现. FutureTask ...
- 【原创】Matlab中plot函数全功能解析
[原创]Matlab中plot函数全功能解析 该帖由Matlab技术论(http://www.matlabsky.com)坛原创,更多精彩内容参见http://www.matlabsky.com 功能 ...
- [译]async/await中阻塞死锁
这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...
- Shiro中session超时页面跳转的处理
问题描述 shiro在管理session后,在session超时会进行跳转,这里有两种情况需要考虑,一种是ajax方式的请求超时,一种页面跳转请求的超时. 本文从这两个方面分别考虑并处理. ajax请 ...
- nginx中的超时配置
nginx.conf配置文件中timeout超时时间设置 client_header_timeout 语法 client_header_timeout time默认值 60s上下文 http serv ...
- Android中Activity处理返回结果的实现方式
大家在网上购物时都有这样一个体验,在确认订单选择收货人以及地址时,会跳转页面到我们存入网站内的所有收货信息(包含收货地址,收货人)的界面供我们选择,一旦我们点击其中某一条信息,则会自动跳转到订单提交界 ...
随机推荐
- test面板1
Ext.onReady(function(){ var myPanel=new Ext.TabPanel({ renderTo:Ex ...
- 2018.12.12 codeforces 938E. Max History(组合数学)
传送门 唉最开始居然把题给看错了. 其实是组合数学傻逼题呢. 题意简述:给出一个数列,定义一个与数列有关的fff函数,fff函数定义如下: 首先f=0,M=1f=0,M=1f=0,M=1,一直重复如下 ...
- NETCLR via C#第四版(文摘)
第1部分 CLR基础 第1章 CLR的执行模型 第2章 生成,打包,部署和管理应用程序及类型 第3章 共享程序集和强命名程序集 第2部分 设计类型 第4章 类型 第5章 基元类型 引用类型和值类型 第 ...
- Tomcat架构解析(三)-----Engine、host、context解析以及web应用加载
上一篇博文介绍了Server的创建,在Server创建完之后,就进入到Engine的创建过程,如下: 一.Engine的创建 1.创建Engine实例 当前次栈顶元素为Service对象,通过Se ...
- boost-字符编码转换:使用conv
Windows下的字符集转换可以使用WideCharToMultiByte/ MultiByteToWideChar,Linux下字符集转换可以使用iconv()函数,下面为使用boost的conv来 ...
- RestTemplate将响应数据转换为具有泛型的类对象
前言: 重要,RestTemplate在SpringBoot项目里即便通过HttpMessageConverters添加了Fastjson且优先级比jackson要高也不会在RestTemplate里 ...
- centos7,配置nginx服务器
安装准备 首先由于nginx的一些模块依赖一些lib库,所以在安装nginx之前,必须先安装这些lib库,这些依赖库主要有g++.gcc.openssl-devel.pcre-devel和zlib-d ...
- REST WebService与SOAP WebService的比较
在SOA的基础技术实现方式中WebService占据了很重要的地位,通常我们提到WebService第一想法就是SOAP消息在各种传输协议上交互.近几年REST的思想伴随着SOA逐渐被大家接受,同时各 ...
- python之深浅copy
深浅copy 1.赋值运算 a = [1, 2, 3] b = a a.append(666) print(b) #output:[1, 2, 3, 666] 对于赋值运算来说,a与b指向的是同一个内 ...
- leaflet入门(三)使用GeoJSON创建矢量图形
# 点对象: function g(feature, layer) { // does this feature have a property named popupContent? if (fea ...