【问题标题】:How to stop waiting for line from System.in?如何停止等待来自 System.in 的线路?
【发布时间】:2016-05-28 04:02:03
【问题描述】:

我有一个从标准输入读取数据的服务。在这里

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在这里不是一个很好的选择......

【问题讨论】:

  • @acdcjunior 很有趣,所以只是定期检查我们是否有数据要完成 readLine 是唯一的方法吗?
  • 当你想要线程到interrupt

标签: java io


【解决方案1】:

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

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

  1. main 方法中的reader 重命名为service
  2. 在终端中运行您的程序。
  3. 当程序等待您按下Enter 时,在另一个终端中:jps -v -- 找到运行ReaderService 的JVM 进程(p),然后执行jstack <p>。这将为您带来 Java 线程转储

你应该得到类似的东西(为简洁起见,省略了其他线程):

"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 &lt; x

也许这种行为更接近你的预期。但它与 I/O 缓冲如何适用于各种类型的流有关。

【讨论】:

    【解决方案2】:

    阅读前检查 reader.ready()。如果为假,则检查关闭标志并休眠100ms左右。

    这并不理想,但我认为这是在 System.in 上超时的最佳方式。不过,这仅适用于 System.in —— 如果您使用的是套接字,那么您应该使用真正的超时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多