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

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

基于Spring实现自定义错误信息返回详解

《基于Spring实现自定义错误信息返回详解》这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景目标实现产出背景Spring 提供了 @RestConChina编程trollerAdvice 用来实现 HTT

Java中的runnable 和 callable 区别解析

《Java中的runnable和callable区别解析》Runnable接口用于定义不需要返回结果的任务,而Callable接口可以返回结果并抛出异常,通常与Future结合使用,Runnab... 目录1. Runnable接口1.1 Runnable的定义1.2 Runnable的特点1.3 使用Ru

Java中Runnable和Callable的区别和联系及使用场景

《Java中Runnable和Callable的区别和联系及使用场景》Java多线程有两个重要的接口,Runnable和Callable,分别提供一个run方法和call方法,二者是有较大差异的,本文... 目录一、Runnable使用场景二、Callable的使用场景三、关于Future和FutureTa

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

springMVC返回Http响应的实现

《springMVC返回Http响应的实现》本文主要介绍了在SpringBoot中使用@Controller、@ResponseBody和@RestController注解进行HTTP响应返回的方法,... 目录一、返回页面二、@Controller和@ResponseBody与RestController

Java中实现订单超时自动取消功能(最新推荐)

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket... 目录1、定时任务2、JDK延迟队列 DelayQueue(1)定义实现Delayed接口的实体类 (

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug