如何停止等待用户输入?

5
我正在构建一个程序来询问乘法,我想设置一个计时器来强制规定时间内回答问题:
  1. 如果在计时器结束之前回答正确:则进行下一次乘法
  2. 如果计时器到达其结束时间,则停止等待用户输入:进行下一次乘法
目前情况下,情况1可以实现,但情况2不行。我考虑使用线程或其他方法从方法中返回;但我不知道如何做。
所以我遇到了一个问题,如果Scanner打开等待输入,如何停止它?我尝试将其放入线程并使用interrupt()或使用布尔标志,但无法停止Scanner。
class Multiplication extends Calcul {    
    Multiplication() {  super((nb1, nb2) -> nb1 * nb2); }   
    @Override
    public String toString() {  return getNb1() + "*" + getNb2(); }
}


abstract class Calcul {

    private int nb1, nb2;
    private boolean valid;
    private boolean inTime = true;
    private boolean answered = false;
    private BiFunction<Integer, Integer, Integer> function;

    Calcul(BiFunction<Integer, Integer, Integer> f) {
        this.nb1 = new Random().nextInt(11);
        this.nb2 = new Random().nextInt(11);
        this.function = f;
    }

    void start() {
        Scanner sc = new Scanner(System.in);
        System.out.println("What much is " + this + " ?");

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                if (!answered) {
                    inTime = false;
                }
            }
        }, 5 * 1000);

        int answer = Integer.parseInt(sc.nextLine());
        if (inTime) {
            checkAnswer(answer);
            timer.cancel();
        }
    }    

    private void checkAnswer(int answer) {
        System.out.println("You said " + answer);
        valid = (function.apply(nb1, nb2) == answer) && inTime;
        answered = true;
    }

    int getNb1() {   return nb1;  }    
    int getNb2() {   return nb2;  }    
    boolean isValid() { return valid; }

     public static void main(String[] args) {
         List<Calcul> l = Arrays.asList(new Multiplication(), new Multiplication(), new Multiplication());
         l.forEach(Calcul::start);
}
}

1
看看这里得到最高票的答案:https://dev59.com/B2kw5IYBdhLWcg3wNX32 这是另一种方法,与Ben在这里提出的不同。 - Jaroslaw Pawlak
@JaroslawPawlak 看起来很棒,而且工作得很好。 - azro
2个回答

3
你可以检查 System.in.available() > 0 来查看是否有可读取的行。只有在返回 true 时才调用 sc.nextLine() 来实际接收输入。
例如:
Scanner sc = new Scanner(System.in);

long sTime = System.currentTimeMillis();
while (System.currentTimeMillis() - sTime < 5000)
{
    if (System.in.available() > 0)
    {
        System.out.println(sc.nextLine());
    }
}

sc.close();

如果有可读内容,此代码将从控制台读取5秒并将其再次打印出来。 注意:实际使用时,您可能会在循环中添加sleep以避免占用过多系统资源。

请注意,这是一种可行的解决方案:available()往往是一种不可靠的方法,它进行一些估计并且可能是错误的。在时间关键的系统中,我可能不会依赖它等等。

此外,为了进一步扩展,此方法依赖于控制台以大多数控制台的方式工作(换句话说:我所知道的所有控制台):只有当用户输入换行符(例如按下回车键)时,该行才会被传递给System.in进行处理。否则,仅键入一个字符即可使available()返回true。


这是“忙等待”,你应该在while循环中添加延迟,并每隔100毫秒检查一次输入。 - Jaroslaw Pawlak
这不是优化过的代码,而是一个例子。显然,不占用所有系统资源是一件好事,但在一个简单的例子范围内,我认为这并不是必要的。但我已经编辑了答案。 - Ben
我认为这至少值得在你的回答中提及,否则我们只会在社区中传播一种不良实践。 - Jaroslaw Pawlak
我已经编辑了main方法,使其更加真实。如果我只是等待,它会在第二个问题崩溃,提示java.io.IOException: Stream closed - azro
2
@azro,你应该在整个程序中只创建一次Scanner ;) - Jaroslaw Pawlak
显示剩余3条评论

0

你尝试过使用close吗?当时间到了,你可以检查输入是否为""并关闭它,这可能会有所帮助。

if(scanner!=null)
{
    scanner.close();
}

问题在于系统永远不会检测到“时间到了”,因为nextLine调用是阻塞的。这只有在你将nextLine运行在单独的线程中时才能起作用,即使如此,在System.in上调用close通常也是一个坏主意。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接