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

How to implement a timeout for a process in Java?

我想为进程实现超时,如果它花费的时间超过 X 秒,我希望它停止并执行 return 语句。 在我的现实世界中,我会调用 REST API,apiCallController() 代表控制器。

根据我在下面的尝试,无论如何,一切都会继续执行。

我该如何做到这一点?

编辑:如果我想要实现的目标有效,那么长的 运行 任务将无法完成,这意味着行

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

永远不会打印,而这一行

response = "Call successful...";

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

String response = "Call aborted...";

但是我还需要return超时后的响应

我一直在测试这个 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;
    }
    
  }

您的任务在打印到控制台后完成,但您的计时器正在等待更多任务,因此仍然是 运行,并且因为它仍然是 运行,所以 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;
    }