(原创)Callable、FutureTask中阻塞超时返回的坑点

2024-02-01 04:18

本文主要是介绍(原创)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(1000);
MyCallable callable2 = new MyCallable(2000);
FutureTask<String> futureTask1 = new FutureTask<String>(callable1);
FutureTask<String> futureTask2 = new FutureTask<String>(callable2);
ExecutorService executor = Executors.newFixedThreadPool(2);
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行代码有超时处理。

 1 import java.util.concurrent.ExecutionException;
 2 import java.util.concurrent.ExecutorService;
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.FutureTask;
 5 import java.util.concurrent.TimeUnit;
 6 import java.util.concurrent.TimeoutException;
 7  
 8 public class FutureTaskExample {
 9  
10     public static void main(String[] args) {
11         MyCallable callable1 = new MyCallable(1000);
12         MyCallable callable2 = new MyCallable(2000);
13  
14         FutureTask<String> futureTask1 = new FutureTask<String>(callable1);
15         FutureTask<String> futureTask2 = new FutureTask<String>(callable2);
16  
17         ExecutorService executor = Executors.newFixedThreadPool(2);
18         executor.execute(futureTask1);
19         executor.execute(futureTask2);
20          
21         while (true) 
22         {
23             try {
24                 if(futureTask1.isDone() && futureTask2.isDone()){
25                     System.out.println("Done");
26                     //shut down executor service
27                     executor.shutdown();
28                     return;
29                 }
30                  
31                 if(!futureTask1.isDone()){
32                 //阻塞futureTask1
33                 System.out.println("FutureTask1 output="+futureTask1.get());
34                 }
35                  
36                 System.out.println("Waiting for FutureTask2 to complete");
37                 String s = futureTask2.get(500L, TimeUnit.MILLISECONDS); //阻塞500毫秒
38                 if(s !=null){
39                     System.out.println("FutureTask2 output="+s);
40                 }
41                 else{
42                     System.out.println("FutureTask2 output is null");
43                 }
44             } catch (InterruptedException | ExecutionException e) {
45                 e.printStackTrace();
46             }catch(Exception e){
47                 //do nothing
48             }
49         }
50          
51     }
52  
53 }

 

如果说是这样的结果,那就错了

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
            }
}
View Code

s的预设值那里有改变:String s="aa";也没发现变为null,是没发生赋值。在异常中s也没有被赋空值。

所以在使用get(long timeout, TimeUnit unit)的时候,变量初始最好能给一个空值,这样就不会产生奇怪的结果,这也是合理的编程习惯。

 

这篇关于(原创)Callable、FutureTask中阻塞超时返回的坑点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/666046

相关文章

springboot体会BIO(阻塞式IO)

使用springboot体会阻塞式IO 大致的思路为: 创建一个socket服务端,监听socket通道,并打印出socket通道中的内容。 创建两个socket客户端,向socket服务端写入消息。 1.创建服务端 public class RedisServer {public static void main(String[] args) throws IOException {

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

Android中如何实现adb向应用发送特定指令并接收返回

1 ADB发送命令给应用 1.1 发送自定义广播给系统或应用 adb shell am broadcast 是 Android Debug Bridge (ADB) 中用于向 Android 系统发送广播的命令。通过这个命令,开发者可以发送自定义广播给系统或应用,触发应用中的广播接收器(BroadcastReceiver)。广播机制是 Android 的一种组件通信方式,应用可以监听广播来执行

struts2中的json返回指定的多个参数

要返回指定的多个参数,就必须在struts.xml中的配置如下: <action name="goodsType_*" class="goodsTypeAction" method="{1}"> <!-- 查询商品类别信息==分页 --> <result type="json" name="goodsType_findPgae"> <!--在这一行进行指定,其中lis是一个List集合,但

多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录 一、LinkedBlockingDeque是什么 二、核心属性详解 三、核心方法详解 addFirst(E e) offerFirst(E e) putFirst(E e) removeFirst() pollFirst() takeFirst() 其他 四、总结 一、LinkedBlockingDeque是什么 首先queue是一种数据结构,一个集合中

多线程篇(阻塞队列- LinkedBlockingQueue)(持续更新迭代)

目录 一、基本概要 1. 构造函数 2. 内部成员 二、非阻塞式添加元素:add、offer方法原理 offer的实现 enqueue入队操作 signalNotEmpty唤醒 删除线程(如消费者线程) 为什么要判断if (c == 0)时才去唤醒消费线程呢? 三、阻塞式添加元素:put 方法原理 图解:put线程的阻塞过程 四、非阻塞式移除:poll方法原理 dequ

数字电路专题:verilog 阻塞赋值和非阻塞赋值

verilog 阻塞赋值 和 非阻塞赋值 “=”阻塞赋值, ”<=”非阻塞赋值。阻塞赋值为执行完一条赋值语句,再执行下一条,可理解为顺序执行,而且赋值是立即执行; 非阻塞赋值可理解为并行执行,不考虑顺序,在 always 块语句执行完成后,才进行赋值。 如下面的阻塞赋值: //代码如下:module top(din,a,b,c,clk);input din;input clk;out

通过Ajax请求后台数据,返回JSONArray(JsonObject),页面(Jquery)以table的形式展示

点击“会商人员情况表”,弹出层,显示一个表格,如下图: 利用Ajax和Jquery和JSONArray和JsonObject来实现: 代码如下: 在hspersons.html中: <!DOCTYPE html><html><head><meta charset="UTF-8"><title>会商人员情况表</title><script type="text/javasc

【NodeJS】Unexpected token (109:0) 返回错误码500

刚开始报错是这样的: Unexpected token call 是什么我没看懂,但我发现 span.label.lable-success 后面的 #[i+1] 写错了,应该是 #{i+1} 改成完这个错误后又是一个错误提示: What? Unexpected token (109:0) 返回错误码500是什么鬼 我先将自己这段源码的 - if ... - else 检查下

多线程篇(阻塞队列- ArrayBlockingQueue)(持续更新迭代)

目录 一、源码分析 1. 先看个关系图 2. 构造方法 3. 核心属性 4. 核心功能 入队(放入数据) 出队(取出数据) 5. 总结 一、源码分析 1. 先看个关系图 PS:先看个关系图 ArrayBlockingQueue是最典型的有界阻塞队列,其内部是用数组存储元素的, 初始化时需要指定容量大小利用 ReentrantLock 实现线程安全。 在生产者