当前位置: 首页 > 知识库问答 >
问题:

Java中带超时的用户输入

谯英彦
2023-03-14

我试图用这个功能构建一个命令行界面:如果用户插入一个输入(在本例中是一个整数)需要15秒以上的时间,该函数会做出一个默认选择(0)。下面的代码是我到目前为止写的,它工作正常。

问题是我想添加一个新功能:如果用户写错了数字(

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

这是我的代码,但我对如何添加功能没有明确的想法,因为我对未来的、可调用的和执行器功能相对较新。如果有人在这方面有更多的经验,我很乐意学习!

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;
    }

共有3个答案

靳举
2023-03-14

下面是一个简化的较短版本,如果用户未键入内容,它将在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);
}
艾俊晖
2023-03-14

是的,所以你想做的是提交未来并调用未来#get(),并使用TimeUnit和Long参数来指示在放弃操作/执行之前要阻止的阈值。

值得注意的是,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();
       }
    }
乐刚毅
2023-03-14

您可以通过使用带标签的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秒后返回0,与当前处理代码的方式相同

Enter your choice in 15 seconds :
0

另一个示例运行:一旦用户输入有效的数字,该方法将返回输入值;否则,它将继续请求有效输入,或在15秒后返回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

注意:使用标记的突破不是强制性的,您可以用传统的突破ing方式来替换它,但这需要您添加更多的代码行。

 类似资料:
  • 问题内容: 可以为用户输入设置计时器吗?等待10秒-进行下一个操作等。例如 问题答案: 不是开箱即用,不。通常,只有当另一个线程关闭基础流或到达输入的末尾时,Reader才会中断read()调用。 由于read()并非具有所有可中断性,因此这成为并发编程问题。知道超时的线程将需要能够中断正在尝试读取输入的线程。 本质上,读取线程将必须轮询Reader的ready()方法,而不是在没有要读取的内容时

  • 可以为用户输入设置计时器吗?等待10秒钟-进行下一步操作等。我的意思是例如

  • 我正在开发一个待办事项列表程序。 我想让用户可以输入日期、时间和关于。我已经知道日期了。我错过的是时间。 但我有很多例外。我错过了什么吗?

  • 编辑问题以包括所需的行为、特定的问题或错误,以及重现问题所需的最短代码。这将帮助其他人回答这个问题。 我正在做一个待办事项列表程序。 我想让用户可以输入日期、时间和关于。我已经有日期了。我缺少的是时间。 但我有很多例外。我有什么遗漏吗?

  • 问题内容: 我尝试创建一个计算器,但由于不知道如何获得用户输入而无法使它工作。 如何获得Java中的用户输入? 问题答案: 你可以根据要求使用以下任何选项。 Scanner class BufferedReader and InputStreamReader classes DataInputStream class 该类中的方法已被弃用。若要获取值,应将先前的解决方案与一起使用 Console

  • 问题内容: 我有一个JComboBox的子类。我尝试使用以下代码添加一个键侦听器。 但是,这不能正确检测用户何时按下一个键。实际上根本没有调用它。我添加的这个监听器是否错误?还有其他添加方式吗? 问题答案: 关键事件不是在框本身上触发的,而是在其编辑器上触发的。您需要将keyListener添加到JComboBox的编辑器中,而不是直接添加到框中: 编辑:固定方法调用。