如何停止等待System.in中的行?

时间:2016-05-28 04:02:03

标签: java io

我有一个从标准输入执行读取的服务。这是

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()不会中断对下一行的玷污。我以为它会抛出IOExecption

有没有办法从另一个线程“中断”这样的服务?也许BufferedReader在这里不是一个很好的选择...

2 个答案:

答案 0 :(得分:2)

如果主线程拥有对它最终尝试关闭的流的独占访问权限,那么您的期望是正确的。但这不是这里的情况,这就是为什么这个程序展示了等待。当您按下回车键(这是行缓冲流,如System.in刷新内容时),程序将按预期退出。

为了说明,请执行以下操作:

  1. reader方法中的main重命名为service
  2. 在终端中运行您的程序。
  3. 当程序在等你按Enter时,在另一个终端:jps -v - 找到运行ReaderService的JVM进程(p),然后执行jstack <p> 1}}。这将为您带来 Java Thread Dump
  4. 你应该得到类似的东西(为简洁省略了其他线程):

    "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已锁定BufferedReader的锁0x000000076ac47f78并已进入关键部分。请注意,t处于RUNNABLE状态,只等待有人按Enter。一旦发生这种情况,事情应该恢复正常,因为最终,main线程应该成功获取锁,如BufferedReader.java源代码(在JDK 1.8中的第522行左右):

    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
    3. 也许这种行为更接近你的期望。但它与I / O缓冲如何适用于各种类型的流有关。

答案 1 :(得分:1)

在阅读之前检查reader.ready()。如果它是假的,那么检查关闭的标志并睡眠100ms左右。

它并不理想,但我认为这是在System.in上超时的最佳方法。这只适用于System.in,但如果您正在为套接字服务,那么您应该使用实际超时。