2

SO上有很多关于中断读取system.in的线程,但我在这里寻找的是一些关于如何最好地编码我想要实现的东西的建议。

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

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

  1. 启动两个线程,它们执行以下操作:

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

  3. 返回用户登录

我对这种方法的问题是调用scanner.nextline()时不会中断,这就是为什么我不加入第 2 步,因为 main会挂起。有没有办法在中断后完成?或者这种方法是否完全矫枉过正,并且有一种更简单的方法来实现合同?thread2thread1.interruptthread1Threadthread1thread2

4

4 回答 4

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 的 Robot 类,因此可能无法与无头终端(?)
  • 假设焦点窗口是控制台窗口。如果焦点在其他地方,ENTER则将为该窗口而不是您的应用程序窗口注册按键。

希望这可以帮助你。我现在真的没主意了。:)

于 2012-10-09T15:15:16.213 回答
3

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

于 2018-01-04T13:46:05.860 回答
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
}
于 2017-01-13T16:47:03.787 回答
-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 ...;
}
于 2017-03-03T12:00:00.803 回答