如何中断scanner.nextline()调用

时间:2012-10-09 15:12:13

标签: java java.util.scanner interrupt

关于中断读取系统的问题有很多线索。但我在这里寻找的是如何最好地编写我想要实现的内容的一些建议。

我有一个getlogin()方法需要执行以下操作:要求用户输入所需的登录环境详细信息,如果6秒后用户没有输入有效值(“实时”或“测试”)然后将userlogin变量设置为“test”并将其返回给调用者。

我对getlogin()实施采用了以下方法:

  1. 启动两个执行以下操作的线程:

    • thread1创建一个扫描仪对象,然后调用scanner.nextline(),并根据用户输入设置变量userlogin。在退出thread1之前中断thread2。
    • thread2等待6秒,如果仍未设置userlogin,则设置userlogin的默认值。在退出thread1之前中断thread2
  2. 加入thread2以阻止主Thread返回userlogin为空

  3. 返回用户登录

  4. 我遇到的问题是scanner.nextline()thread2调用thread1.interrupt时不会中断,这就是为什么我不在步骤2中加入thread1作为主Thread会挂起。 在thread1中断后,有没有办法让thread2完成?或者这种方法是否完全过度,并且有一种更简单的方法来实现合同?

4 个答案:

答案 0 :(得分:4)

最简单的解决方案是在“读取”线程中公开底层流,并从超时线程中关闭该流。这应该会中断读取并引发异常。处理此异常,您应该能够继续执行逻辑。唯一的问题是你将无法再次重复使用相同的流。不幸的是,没有简单的方法来处理阻塞系统调用的中断。

修改

遵循完全不同的推理方式;鉴于我们无法关闭输入流只是为了打断它,我能想到的唯一方法就是使用Robot类提供的“编程用户输入”功能。这是一个适合我的例子:

import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ConsoleTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        new TimeoutThread().start();
        new ReaderThread().start();
    }

}

class ReaderThread extends Thread {

    @Override
    public void run() {
        System.out.print("Please enter your name: ");
        try(Scanner in = new Scanner(System.in)) {
            String name = in.nextLine();
            if(name.trim().isEmpty()) {
                name = "TEST"; // default user name
            }
            System.out.println("Name entered = " + name);
        }
    }

}

class TimeoutThread extends Thread {

    @Override
    public void run() {
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(5));
            Robot robot = new Robot();
            robot.keyPress(KeyEvent.VK_ENTER);
            robot.keyRelease(KeyEvent.VK_ENTER);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

}

上面的代码使用的逻辑是,一旦超时到期,我们就会模拟一个新行,这将导致“name”变量为空。然后我们有一个检查,它执行必要的逻辑并设置适当的用户名。

关于上述方法的问题在于:

  • 使用机器人类AWT,因此可能无法与无头终端(?)
  • 配合使用
  • 假设焦点窗口是控制台窗口。如果焦点位于其他位置,则会为该窗口注册ENTER按键,而不是应用程序窗口。

希望这会帮助你。我现在真的没有想法了。 :)

答案 1 :(得分:0)

还可以使用FutureTask和lambda表达式:

FutureTask<String> readNextLine = new FutureTask<String>(() -> {
  return scanner.nextLine();
});

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(readNextLine);

try {
  String token = readNextLine.get(5000, TimeUnit.MILLISECONDS);
  ...
} catch (TimeoutException e) {
  // handle time out
}

答案 2 :(得分:0)

如果有要读取的字节,为什么不用System.in.available()轮询?它是非阻塞的:可以调用Scanner.nextLine(),当它确定有效并且不阻塞时阻塞它。

答案 3 :(得分:-1)

geri的另一个版本的答案是:

ExecutorService executor = Executors.newFixedThreadPool(1);

Future<String> future = executor.submit(() -> {
    try (Scanner in = new Scanner(System.in)) {
        return in.nextLine();
    }
});

try {
    return future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e1) {
    return ...;
}