(原创)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

相关文章

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

统一返回JsonResult踩坑的记录

《统一返回JsonResult踩坑的记录》:本文主要介绍统一返回JsonResult踩坑的记录,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录统一返回jsonResult踩坑定义了一个统一返回类在使用时,JsonResult没有get/set方法时响应总结统一返回

MyBatis设计SQL返回布尔值(Boolean)的常见方法

《MyBatis设计SQL返回布尔值(Boolean)的常见方法》这篇文章主要为大家详细介绍了MyBatis设计SQL返回布尔值(Boolean)的几种常见方法,文中的示例代码讲解详细,感兴趣的小伙伴... 目录方案一:使用COUNT查询存在性(推荐)方案二:条件表达式直接返回布尔方案三:存在性检查(EXI

Python函数返回多个值的多种方法小结

《Python函数返回多个值的多种方法小结》在Python中,函数通常用于封装一段代码,使其可以重复调用,有时,我们希望一个函数能够返回多个值,Python提供了几种不同的方法来实现这一点,需要的朋友... 目录一、使用元组(Tuple):二、使用列表(list)三、使用字典(Dictionary)四、 使

Python处理函数调用超时的四种方法

《Python处理函数调用超时的四种方法》在实际开发过程中,我们可能会遇到一些场景,需要对函数的执行时间进行限制,例如,当一个函数执行时间过长时,可能会导致程序卡顿、资源占用过高,因此,在某些情况下,... 目录前言func-timeout1. 安装 func-timeout2. 基本用法自定义进程subp

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

前端下载文件时如何后端返回的文件流一些常见方法

《前端下载文件时如何后端返回的文件流一些常见方法》:本文主要介绍前端下载文件时如何后端返回的文件流一些常见方法,包括使用Blob和URL.createObjectURL创建下载链接,以及处理带有C... 目录1. 使用 Blob 和 URL.createObjectURL 创建下载链接例子:使用 Blob

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下: