用户输入超时 Java

User input with a timeout in Java

我正在尝试构建具有此功能的命令行界面:如果用户花费超过 15 秒来插入输入(在本例中为整数),则该函数会做出默认选择 (0)。下面的代码是我到目前为止写的,它工作正常。

问题是我想添加一个新功能:如果用户输入了错误的数字(<0 或 >range),控制台应该打印类似 ("Wrong choice, you have to pick an integer between 0 - "+ range);

的内容

但是,当控制台打印消息时,计时器仍应 运行 并在 15 秒后结束此循环,以防用户不断输入错误的数字。如果用户最终得到一个正确的数字,它应该立即中断循环。

这是我的代码,但我对如何添加功能没有清晰的想法,因为我对 Future、Callable 和 Executor 功能比较陌生。如果谁有这方面的经验,我很乐意学习!

private int getChoiceWithTimeout(int range){
       Callable<Integer> k = () -> new Scanner(System.in).nextInt();
       Long start= System.currentTimeMillis();
       int choice=0;
       ExecutorService l = Executors.newFixedThreadPool(1);  ;
       Future<Integer> g;
       System.out.println("Enter your choice in 15 seconds :");
       g= l.submit(k);
       while(System.currentTimeMillis()-start<15*1000 && !g.isDone()){
           // Wait for future
       }
       if(g.isDone()){
           try {
               choice=g.get();
           } catch (InterruptedException | ExecutionException e) {
               e.printStackTrace();
           }
       }
       g.cancel(true);
       return choice;
    }

是的,所以您要做的是提交未来并调用 Future#get() 并使用 TimeUnit 和 Long 参数来指示在放弃 operation/execution 之前要阻止的阈值。

值得注意的是,ThreadPoolExecutor 不应该这样使用。它应该在方法之外定义并重新使用,然后在应用程序终止或不再需要时关闭。

    private int getChoiceWithTimeout(int range){
       Callable<Integer> callable = () -> new Scanner(System.in).nextInt();

       ExecutorService service = Executors.newFixedThreadPool(1);

       System.out.println("Enter your choice in 15 seconds :");

       Future<Integer> inputFuture = service.submit(callable);

       try {
           return inputFuture.get(15, TimeUnit.SECONDS);
       } catch (InterruptedException e) {
           throw new IllegalStateException("Thread was interrupted", e);
       } catch (ExecutionException e) {
           throw new IllegalStateException("Something went wrong", e);
       } catch (TimeoutException e) {
           // tell user they timed out or do something
           throw new IllegalStateException("Timed out! Do something here OP!");
       } finally {
           service.shutdown();
       }
    }

您可以使用 labelled break(下面给出的代码中的 done:)和一个 boolean 变量(下面给出的代码中的 valid)来跟踪输入是否有效。

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(getChoiceWithTimeout(10));
    }

    static int getChoiceWithTimeout(int range) {
        Callable<Integer> k = () -> new Scanner(System.in).nextInt();
        Long start = System.currentTimeMillis();
        int choice = 0;
        boolean valid;
        ExecutorService l = Executors.newFixedThreadPool(1);
        Future<Integer> g;
        System.out.println("Enter your choice in 15 seconds :");
        g = l.submit(k);
        done: while (System.currentTimeMillis() - start < 15 * 1000) {
            do {
                valid = true;
                if (g.isDone()) {
                    try {
                        choice = g.get();
                        if (choice >= 0 && choice <= range) {
                            break done;
                        } else {
                            throw new IllegalArgumentException();
                        }
                    } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                        System.out.println("Wrong choice, you have to pick an integer between 0 - " + range);
                        g = l.submit(k);
                        valid = false;
                    }
                }
            } while (!valid);
        }

        g.cancel(true);
        return choice;
    }
}

一个示例 运行: 不要输入任何内容,该方法将在 15 秒后 return 和 0,就是这样当前正在使用您的代码

Enter your choice in 15 seconds :
0

另一个示例运行: 一旦用户输入有效数字,该方法将return 与输入的值;否则,它将在 15 秒后继续请求有效输入或 return 0

Enter your choice in 15 seconds :
a
Wrong choice, you have to pick an integer between 0 - 10
12
Wrong choice, you have to pick an integer between 0 - 10
5
5

注意: 使用标记的 break 不是强制性的,您可以将其替换为传统的 breaking 方式,但这需要您添加多几行代码。

这是一个简化的较短版本,如果用户没有输入内容,它会在 n 秒后超时:

private static final ExecutorService l = Executors.newFixedThreadPool(1);

private static String getUserInputWithTimeout(int timeout) {
    Callable<String> k = () -> new Scanner(System.in).nextLine();
    LocalDateTime start = LocalDateTime.now();
    Future<String> g = l.submit(k);
    while (ChronoUnit.SECONDS.between(start, LocalDateTime.now()) < timeout) {
        if (g.isDone()) {
            try {
                String choice = g.get();
                return choice;
            } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                logger.error("ERROR", e);
                g = l.submit(k);
            }
        }
    }
    logger.info("Timeout...");
    g.cancel(true);
    return null;
}

public static void main(String[] args) {
    logger.info("Input the data in 10 seconds or less...");
    String res = getUserInputWithTimeout(10); // 10s until timeout
    if(res != null) {
        logger.info("user response: [" + res + "]");
    }
    System.exit(0);
}