【问题标题】:How to stop a thread waiting in a blocking read operation in Java?如何停止在 Java 中的阻塞读取操作中等待的线程?
【发布时间】:2011-05-10 02:38:32
【问题描述】:

我有一个执行以下代码的线程:

public void run() {
    try {
        int n = 0;
        byte[] buffer = new byte[4096];
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
            out.flush();
        }
    } catch (IOException e) {
        System.out.println(e);
    }
}

其中inSystem.in。我怎样才能优雅地停止这样的线程?关闭 System.in 或使用 Thread.interrupt 似乎都不起作用。

【问题讨论】:

  • 使用中断会发生什么?
  • @tulskiy:线程继续运行,直到手动输入 Ctrl-D (EOF)。

标签: java multithreading stream


【解决方案1】:

这是因为读取 System.in (InputStream) 是一个阻塞操作。

看这里Is it possible to read from a InputStream with a timeout?

【讨论】:

  • 谢谢。使用从System.in 中提取的NIO FileChannel 可以通过关闭System.in 来停止线程。关闭System.in 会导致AsynchronousCloseException 中断阻塞FileChannel.read
【解决方案2】:

您偶然发现了一个 9 岁的 bug 没有人愿意修复。他们说this bug report 中有一些解决方法。很可能,您需要找到其他方法来设置超时(忙于等待似乎是不可避免的)。

【讨论】:

  • 有趣的是,当我偶然发现这一点时,它的行为更加奇怪。中断有效,但花了 2.5 分钟! (ubuntu 18.04)
【解决方案3】:

你可以使用 available() 方法(它是非阻塞的)来检查是否有预先读取的内容。

在伪java中:

//...
while(running)
{
    if(in.available() > 0)
    {
        n = in.read(buffer);
        //do stuff with the buffer
    }
    else
    {
        Thread.sleep(500);
    }
}
//when running set to false exit gracefully here...

【讨论】:

  • Abritraary number,如果你不睡觉,你最终会进入一个热循环,让 CPU 等待读取可用字节。我的假设是这个线程将花费大部分时间等待输入。
  • 使用available 几乎可以解决问题。问题是如果 EOF available 返回 0 并且线程继续执行。 +1 补偿不公平 -1 =)
  • 我只是想到了响应能力...... PeterMmms 链接说明了一切:stackoverflow.com/questions/804951/…
  • 谢谢!你的回答刚刚救了我的命!
【解决方案4】:

在其他线程中关闭流是否安全? 这个对我有用。在这种情况下,in.read(...) 会抛出异常 SocketException

【讨论】:

    【解决方案5】:

    如果您想给用户一些时间来输入数据 - 可能允许覆盖默认值或中断一些自动化过程 - 然后先等待并在暂停后检查可用输入:

    System.out.println("Enter value+ENTER within 5 Seconds to override default value: ");
    try{
      Thread.sleep(5000);
    } catch {InterruptedException e){}
    
    try{
      int bytes = System.in.available();
      if (bytes > 0) {
        System.out.println("Using user entered data ("+size+" bytes)");
      } else {
        System.out.println("Using default value"); 
      }
    } catch(IOException e) { /*handle*/ }
    

    【讨论】:

      【解决方案6】:

      我今天遇到了同样的问题,我就是这样解决的,使用in.ready()

      public void run() {
          String line;
          // Some code
      
          while(!Thread.currentThread().isInterrupted()){
              try {
                  if (in.ready()) {
                      line = in.readLine();
                  } 
              } catch (Exception e) {
                  try {
                      Thread.currentThread().wait(500);
                  } catch (InterruptedException e1) {
                      // Do what we want when thread is interrupted
                  }
              }
          }
      }
      

      【讨论】:

      • 很有趣,但是在没有什么可读取的情况下一直执行循环不是要占用大量 CPU 吗?
      • 实际上这就是为什么我让线程每次迭代都等待一段时间。但你是对的,它没有它应该的那么有效。现在取决于软件的用途。
      • 很确定如果流中没有换行符,这仍然会挂起。如果有单个字节, in.ready() 返回 true,in.readLine() 读取它,然后继续(永远)等待换行符。
      【解决方案7】:

      您可以为此使用外部标志

      boolean flag = true;
      
      
      public void run() { 
          try { 
              int n = 0; 
              byte[] buffer = new byte[4096]; 
              while ((n = in.read(buffer)) != -1 && flag) { 
                  out.write(buffer, 0, n); 
                  out.flush(); 
              } 
          } catch (IOException e) { 
              System.out.println(e); 
          } 
      } 
      

      【讨论】:

      • 这不会中断已经阻塞的调用,所以它不能解决问题。
      猜你喜欢
      • 2010-12-21
      • 2019-09-02
      • 2013-07-05
      • 2018-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多