Отмена ввода пользователя - PullRequest
0 голосов
/ 10 марта 2020

Допустим, у меня есть этот бит кода в java:

Scanner s = new Scanner(System.in);

Long time = System.nanoTime() + 10000000000L; //10 seconds in the future
String input = s.nextLine();
//What to do from here?

System.out.println("Timed out");

Как я могу отменить пользовательский ввод, если он еще ничего не вводил и текущее время больше time ? Есть ли способ, позволяющий пользователю что-то вводить, но и запускать код в течение этого времени? nextLine(), кажется, приостанавливает выполнение, пока пользователь не введет что-либо.

1 Ответ

0 голосов
/ 10 марта 2020

многие «блокируют, пока что-то не происходит» методы (такие как java.lang.Thread 'wait метод) имеют перегруженный вариант, в котором можно указать время ожидания. Однако большинство методов ввода / вывода (например, nextLine сканера или read входного потока) не делают.

Спецификация этих типов указывает на то, что они являются прерывистыми или нет намеренно не определены; java хочет работать на как можно большем количестве платформ, а наименьший общий знаменатель - «нет, не прерываемый», поэтому он указан именно таким образом.

Однако на большинстве платформ да, вы может прервать его.

Таким образом, решение:

Запустите второй поток, передайте ему ссылку на ваш поток и скажите, чтобы он прерывал этот поток по истечении определенного времени.

Поток, который прерывается, просто «поднимает флаг прерывания», ничего более (вы можете проверить с помощью if (Thread.interrupted()) ...;, если хотите, который также очищает этот флаг. Однако, различные методы, встроенные в java, будут немедленно выход, если вы вызываете их, когда этот флаг поднят, и будет отменен на месте, если вы поднимите флаг, когда они блокируют.

Любой метод, который объявлен для выброса InterruptedException, например Thread.sleep, равен гарантированно будет иметь такое поведение. Любой другой метод блокировки может или не может (вернуться ко всему этому «общему знаменателю всех платформ java работает на»). Другими словами , поскольку вы не совсем знаете, где будет находиться ваш код, вам нужно иметь дело с различными сценариями ios. Что, если прерывание происходит, когда вы ожидаете ввода (скорее всего, readLine завершается броском чего-то, что вы можете поймать), что если оно происходит сразу после (этот флаг будет установлен; вам нужно очистить его в какой-то момент ) и как вы 'отменяете отмену' - если пользователь печатает, вы хотите, чтобы этот тикающий поток бомб с часовым механизмом закончился сам. Вы можете сделать это, просто установив переменную, которую проверяет поток временной бомбы, даже если он проверяет ее только после истечения времени (Время истекло! О, поток, который я должен прервать, когда-то сказал мне, пока я ждал тайм-аута, который это больше не нужно, поэтому я просто закончу сейчас).

Все довольно сложно. Может быть, есть какая-то библиотека, чтобы сделать это намного проще для вас, но я лично не знаю о ней.

public class InterruptMeThread extends Thread {
    private final Thread interruptMe;
    private final long millis;
    private volatile boolean nevermind;

    public InterruptMeThread(Thread interruptMe, long millis) {
        this.interruptMe = interruptMe;
        this.millis = millis;
    }

    @Override public void run() {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            // Who interrupts the interruptors? Let's assume this means
            // my interruption services are no longer desired and exit.
            return;
        }

        if (!nevermind) interruptMe.interrupt();
    }

    public void nevermind() {
        this.nevermind = true;
        Thread.interrupted(); // clear the flag if it is raised.
}

и используется:

InterruptMeThread imt = new InterruptMeThread(Thread.currentThread(), 10000);
imt.start();
try {
    reader.nextLine();
} catch (IOException e) {
    // you'd have to test what kind of exception falls out of interrupted...
    // but something will.
    // for now, to debug it so we can see it...
    throw new RuntimeException(e); // or just let it bubble up, either way works.
}
imt.nevermind();
...