Java: как прервать чтение потока из System.in
У меня есть поток Java:
class MyThread extends Thread {
@Override
public void run() {
BufferedReader stdin =
new BufferedReader(new InputStreamReader(System.in));
String msg;
try {
while ((msg = stdin.readLine()) != null) {
System.out.println("Got: " + msg);
}
System.out.println("Aborted.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
В другой теме, как мне прервать stdin.readline()
вызовите в этой теме, чтобы эта тема печатала Aborted.
? я пытался System.in.close()
, но это не имеет никакого значения, stdin.readline()
все еще блокирует
Я заинтересован в решениях без
- заняты ожиданием (потому что это сжигает 100% CPU);
- спит (потому что тогда программа не мгновенно реагирует на
System.in
).
7 ответов
Моя первая реакция заключается в том, что нить и System.in
на самом деле не ходите вместе.
Итак, сначала разделите это так, чтобы код потока не касался статического System.in
,
Поток читает из InputStream
и переходит в буфер. Передать InputStream
в ваш существующий поток, который читает из буфера, но также проверяет, что вы не прервали.
Бюллетень Heinz Kabutz показывает, как прервать System.in
читает с использованием буфера и ExecutorService
,
Теперь я не знаю, протекает ли этот подход, не является переносимым или имеет неочевидные побочные эффекты. Лично я бы неохотно его использовал.
Возможно, вы сможете что-то сделать с каналами NIO и дескрипторами файлов - мои собственные эксперименты с ними не дали никаких результатов.
Как насчет...
private static BufferedReader stdInCh = new BufferedReader(
new InputStreamReader(Channels.newInputStream((
new FileInputStream(FileDescriptor.in)).getChannel())));
Нить где stdInch.readline()
теперь вызывается, и readline() выдаст java.nio.channels.ClosedByInterruptException
,
никогда не может бросить, потому что этого нет в его контракте. (Вы должны продолжить проверку перед чтением.) Фактически, этого исключения нет в контракте большинства классов и методов! (И по большей части они не утруждают себя звонками.)
Более серьезная проблема заключается в том, что (в контракте она есть) тоже не гарантирует успеха. Некоторые каналы лгут о том, что их можно прерывать, в то время как классы, использующие каналы, могут все равно заблокироваться. Например, вы можете подумать
Channels.newChannel(System.in)
доставит вам хороший
InterruptibleChannel
. Но это ложь, о которой свидетельствуют комментарии к исходному коду «Непрерывно» и «Блокировать не более одного раза» (OpenJDK 16). (Да, действительно блокирует, я проверял. Нелепо!)
Комбинация, которая, как я обнаружил, работает, - это использовать
new FileInputStream(FileDescriptor.in).getChannel()
с участием
Scanner
.
Scanner scanner = new Scanner(new FileInputStream(FileDescriptor.in).getChannel())
while (!scanner.hasNextLine())
Thread.sleep(100); // Internally checks Thread.interrupted() and throws InterruptedException
String line = scanner.nextLine()
Это действительно не должно быть проблемой. Несложно написать класс, проверяющий
System.in.available()
,
Thread.interrupted()
, бросает
InterruptedException
и т. д. 🤷 Даже сканер не проверяет
available
если дать
InputStream
, или
Channel
в режиме блокировки (начиная с OpenJDK 16). Прокомментируйте, если вы знаете о нормальном классе, который знает.
JavaDoc для BufferedReader.readLine:
Возвращает: строка, содержащая содержимое строки, не включая символы окончания строки, или ноль, если достигнут конец потока
Исходя из этого, я не думаю, что он когда-либо вернет ноль (может ли System.in на самом деле быть закрытым, я не думаю, что он когда-либо вернет конец потока?), Поэтому цикл while не завершится. Обычный способ остановить поток - либо использовать логическую переменную в условии цикла и изменить ее извне потока, либо вызвать метод interrupt() -объекта объекта Thread (работает только в том случае, если поток является wait():ing или sleep():ing, или в методе блокировки, который выбрасывает InterruptedException). Вы также можете проверить, был ли поток прерван с помощью isInterrupted ().
Изменить: вот простая реализация с использованием isInterrupted()
а также interrupt()
, Основной поток ждет 5 секунд, прежде чем прерывать рабочий поток. В этом случае рабочий поток в основном занят ожиданием, так что это не так хорошо (циклически все время и проверка stdin.ready()
Вы, конечно, можете оставить рабочий поток на некоторое время, если вход не готов):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class MyThreadTest
{
public static void main(String[] args)
{
MyThread myThread = new MyThread();
myThread.start();
try
{
Thread.sleep(5000);
}
catch(InterruptedException e)
{
//Do nothing
}
myThread.interrupt();
}
private static class MyThread extends Thread
{
@Override
public void run()
{
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String msg;
while(!isInterrupted())
{
try
{
if(stdin.ready())
{
msg = stdin.readLine();
System.out.println("Got: " + msg);
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
System.out.println("Aborted.");
}
}
}
Кажется, нет никакого способа фактически прервать BufferedReader, если он заблокирован на readline, или, по крайней мере, я не смог его найти (используя System.in).
Как насчет определения поля в приведенном выше определении класса потока, например:
class MyThread extends Thread {
protected AtomicBoolean abortThread = new AtomicBoolean(false);
public void doAbort()
{
this.abortThread.set(true);
}
@Override public void run()
{
...
if (this.abortThread.get())
{
...something like break loop...
}
}
}
Ты пытался:
Thread myThread = new MyThread();
myThread.start();
// your code.
myThread.interrupt();
Метод прерывания создает исключение InterrupedtException, и после этого вы можете обработать код.