2016-05-28 2 views
0

У меня есть служба, которая выполняет чтение со стандартного ввода. ЗдесьКак остановить ожидание линии от System.in?

public class ReaderService{ 
    private BufferedReader reader; 
    private volatile boolean closed; 

    public void start(){ 
     while(!closed){ 
      reader = new BufferedReader(new InputStreamReader(System.in)); 
      try { 
       System.out.println(reader.readLine()); 
      } catch (IOException e) { 
       System.out.println("IOException"); 
      } 
     } 
    } 

    public void stop(){ 
     try { 
      reader.close(); 
     } catch (IOException e) { 
      System.exit(1); 
     } 
     closed = true; 
    } 
} 

Я использую его следующим образом:

public static void main(String[] args) throws InterruptedException{ 
    Thread t = new Thread(new Runnable() { 
     public void run() { 
      reader.start(); 
     } 
    }); 
    t.start(); 
    Thread.sleep(5000); 
    reader.stop(); 
} 

Вопрос в том, что вызов ReaderService::stop() не прерывает wainting для следующей строки. Я думал, что это бросит IOExecption.

Есть ли способ «прервать» такую ​​услугу из другого потока? Может быть, BufferedReader здесь не совсем хороший выбор ...

+0

Может быть полезным: http://stackoverflow.com/questions/6008177/java-how-to-abort-a-thread-reading-from-system-in – acdcjunior

+0

@acdcjunior Интересно, поэтому просто проверяйте периодически, если мы есть ли данные для завершения readLine - это только путь? – stella

+0

, когда вы хотите, чтобы поток 'прерывал' – emotionlessbananas

ответ

2

Ваши ожидания верны, если основной поток имел эксклюзивный доступ к потоку, который в конечном итоге пытается закрыть. Но это не так, и поэтому эта программа демонстрирует ожидание. Когда вы нажимаете кнопку ввода (то есть, когда строка буферизует, поток, такой как System.in, сбрасывает содержимое), программа завершается так, как ожидалось.

Чтобы проиллюстрировать это, выполните следующие действия:

  1. переименовывать reader в методе main к service.
  2. Запустите программу в терминале.
  3. Пока программа ждет вас, чтобы нажать Enter, в другом терминале: jps -v - найдите процесс JVM (p), который запускается ReaderService, а затем выполните jstack <p>. Это приносит вам Java Thread Dump.

Вы должны получить что-то вроде (другие потоки опущенных для краткости):

"main" #1 prio=5 os_prio=31 tid=0x00007fef4d803000 nid=0xf07 
waiting for monitor entry [0x000000010b7a3000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
    at java.io.BufferedReader.close(BufferedReader.java:522) 
    - waiting to lock <0x000000076ac47f78> (a java.io.InputStreamReader) 
    at ReaderService.stop(ReaderService.java:19) 
    at ReaderService.main(ReaderService.java:34) 

"Thread-0" #10 prio=5 os_prio=31 tid=0x00007fef4c873800 nid=0x5503 runnable [0x000000012b497000] 
    java.lang.Thread.State: RUNNABLE 
    at java.io.FileInputStream.readBytes(Native Method) 
    at java.io.FileInputStream.read(FileInputStream.java:255) 
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:284) 
    at java.io.BufferedInputStream.read(BufferedInputStream.java:345) 
    - locked <0x000000076ab1bf10> (a java.io.BufferedInputStream) 
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) 
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) 
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) 
    - locked <0x000000076ac47f78> (a java.io.InputStreamReader) 
    at java.io.InputStreamReader.read(InputStreamReader.java:184) 
    at java.io.BufferedReader.fill(BufferedReader.java:161) 
    at java.io.BufferedReader.readLine(BufferedReader.java:324) 
    - locked <0x000000076ac47f78> (a java.io.InputStreamReader) 
    at java.io.BufferedReader.readLine(BufferedReader.java:389) 
    at ReaderService.start(ReaderService.java:10) 
    at ReaderService$1.run(ReaderService.java:29) 
    at java.lang.Thread.run(Thread.java:745) 

Как вы можете видеть, после того, как main нити делается спать, он переходит в BLOCKED состояние ждет, чтобы получить блокировку на BufferedReader, который представляет собой изменяемое разделяемое состояние (которое в этом случае обходит нить main потоком t). Как и ожидалось, нить t уже заблокировала блокировку BufferedReader0x000000076ac47f78 и вошла в критический раздел. Обратите внимание, что t находится в RUNNABLE состоянии, только ожидая, что кто-то нажмет Enter. Как только это произойдет, все должно вернуться к нормальной жизни, так как в конце концов, main нить должна добиться успеха в приобретении замка, как в BufferedReader.java исходном коде (вокруг линии 522 в JDK 1.8):

public void close() throws IOException { 
    synchronized (lock) { 
     if (in == null) 
      return; 
     try { 
      in.close(); 
     } finally { 
      in = null; 
      cb = null; 
     } 
    } 
} 

Вы также увидите некоторое интересное поведение если вы это сделаете:

  1. На терминале Unix создайте текстовый файл x с некоторым текстом в нем.
  2. Пробег java ReaderService < x.

Возможно, это поведение ближе к вашему ожиданию. Но это связано с тем, как буферизация ввода-вывода работает для различных типов потоков.

1

Check reader.ready() перед тем, как читать. Если это неверно, проверьте флаг закрытого типа и сон за 100 мс или около того.

Это не идеальный вариант, но я думаю, что это лучший способ тайм-аута на System.in. Это применимо только для System.in, но если вы используете сокет, вы должны использовать реальный тайм-аут.