如何在 Java 中为进程实现超时?

Posted

技术标签:

【中文标题】如何在 Java 中为进程实现超时?【英文标题】:How to implement a timeout for a process in Java? 【发布时间】:2021-07-03 20:38:57 【问题描述】:

我想为一个进程实现超时,如果它需要超过 X 秒,我希望它停止并执行 return 语句。 在我的实际使用中,我会调用一个 REST API,apiCallController() 代表控制器。

根据我在下面尝试的所有内容,无论如何都会继续执行。

我该如何解决这个问题?

编辑:如果我想要实现的工作,长时间运行的任务将无法完成,这意味着该行

System.out.println("End http/SSH stuff...");

永远不会打印,而这一行

response = "Call successful...";

也不会执行,将响应变量保留为最初初始化的状态

String response = "Call aborted...";

但我仍然需要在超时后返回响应

我一直在这个 Java fiddle 中进行测试(你可以直接粘贴代码):https://javafiddle.leaningtech.com/

谢谢。

import java.util.*;
import java.lang.*;

  public class JavaFiddle
  
    public static void main(String[] args)
    
        String response = apiCallController();
        System.out.println(response);
    
    
    public static String apiCallController() 
        System.out.println("creepy...\n");
        int timeoutSeconds = 2;
        int longRunningTaskDurationSeconds = 5;
        String response = "Call aborted...";

        try 
        
            new Timer().schedule( 
                new TimerTask() 
                    @Override
                    public void run() 
                        System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                        // System.exit(0); This guy shut tomcat down x_X
                        return;
                    
                , 
                timeoutSeconds * 1000
            );
            
            System.out.println("Start http/SSH stuff...");
            Thread.sleep(longRunningTaskDurationSeconds * 1000);
            System.out.println("End http/SSH stuff...");
            
            response = "Call successful...";

         
        catch(InterruptedException e)
        
            System.out.println(e);
        

        System.out.println("\npasta...");
        
        return "\n" + response;
    
    
  

编辑 2:根据接受的答案,我只是重构了一点:

import java.util.*;
import java.lang.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;

  public class JavaFiddle
  
    public static void main(String[] args)
    
        System.out.println("creepy...\n\n");

        String response = apiCallController();
        System.out.println(response);

        System.out.println("\n\npasta...");
        
    
    
    public static String apiCallController() 
        String response = "Stuff TIMED out...";
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        Callable<String> r = () -> 
            try 
                System.out.println("Start http/SSH stuff...");
                TimeUnit.SECONDS.sleep(5);
                System.out.println("End http/SSH stuff...");
                return "Stuff COMPLETED successfully...";
             catch (InterruptedException e) 
                throw e;
            
        ;
        Future<String> task = executor.submit(r);

        try 
            response = task.get(3, TimeUnit.SECONDS);
         catch(InterruptedException | TimeoutException e) 
            task.cancel(true);
         catch (ExecutionException e) 
            e.printStackTrace();
        

        // Need to shutdown executor (think of it is master thread here)
        // You may want to control this behaviour outside of this function call
        executor.shutdown();

        return "\n" + response;
    
    
  

【问题讨论】:

timeout*1000 是什么? 我正在将秒转换为毫秒。 你见过Asynchronous timeouts with CompletableFutures in Java 8 and Java 9吗? 对了,你可以把timeoutSeconds * 1000换成更明显的Duration.ofSeconds( timeoutSeconds ).toMillis()。更好的是,将 timeoutSeconds 的类型更改为名为 timeoutDuration 对象。 【参考方案1】:

您的任务在打印到控制台后完成,但您的计时器正在等待更多任务,因此仍在运行,因为它仍在运行,main 函数不会退出。

如果您没有其他任务,您需要cancel您的计时器。

try
        
            Timer timer = new Timer();
            timer.schedule(
                    new TimerTask() 
                        @Override
                        public void run() 
                            System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                        
                    ,
                    timeoutSeconds * 1000
            );

            System.out.println("Start http/SSH stuff...");
            Thread.sleep(longRunningTaskDurationSeconds * 1000);
            System.out.println("End http/SSH stuff...");

            response = "Call successful...";

            timer.cancel();

        
        catch(InterruptedException e)
        
            System.out.println(e);
        

        System.out.println("\npasta...");

        return "\n" + response;

编辑

由于问题已根据实际用例进行了更新,因此我在此处添加了一个编辑以建议该用例的答案(之前的部分可能与现在的问题无关)。

这是我对您问题的解决方案,看看。我已经使用FutureScheduledExecutorService 来实现它。

public static String apiCallController() 
        System.out.println("creepy...\n");
        
        String response = "Call aborted...";

        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

        Callable<String> r = () -> 
            try 
                // To mimick the actual call
                TimeUnit.SECONDS.sleep(5);
                System.out.println("call successful...");
                return "Some response";
             catch (InterruptedException e) 
                System.out.println("Timeout reached, aborting... (This is where I want everything to stop without killing JVM/Tomcat)");
                throw e;
            
        ;

        Future<String> task = executor.submit(r);

        try
        
            System.out.println("Start http/SSH stuff...");
            
            //Let's just wait for 3 secs for response to arrive
            response = task.get(3, TimeUnit.SECONDS);

            System.out.println("End http/SSH stuff...");

            response = "Call successful...";

        
        catch(InterruptedException | TimeoutException e)
        
            // cancelling a task, either it was interrupted (sleep call can be interrupted) or its timeout
            task.cancel(true);
        catch (ExecutionException e) 
            //Something went wrong horribly
            e.printStackTrace();
        

        System.out.println("\npasta...");

        // Need to shutdown executor (think of it is master thread here)
        // You may want to control this behaviour outside of this function call
        executor.shutdown();

        return "\n" + response;
    

【讨论】:

我试图阻止这些行不执行: System.out.println("End http/SSH stuff..."); response = "调用成功...";.他们仍然使用您的解决方案。 请用此信息更新您的问题,谢谢。

以上是关于如何在 Java 中为进程实现超时?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Dart 中为 MultiPart 请求设置超时?

如何在android中为jsoup设置用户代理和连接超时

如何在Asp.net Core的登录过程中为“记住我”设置单个cookie超时?

Java进程获取输出并设置超时

在 Java 中为异步调用添加超时

java - 如何在java android中为Gridview的父类BaseAdapter实现viewBinding?