【问题标题】:How can I read from a BufferedReader in Java without blocking?如何从 Java 中的 BufferedReader 读取而不阻塞?
【发布时间】:2012-06-30 11:41:25
【问题描述】:

我想向服务器发送一个命令,看看我是否得到响应。

现在我正在使用BufferedReaderreadline() 函数,该函数会一直阻塞,直到有来自服务器的响应,但我要做的就是首先验证服务器是否有响应。

我尝试使用ready()reset() 来避免此阻塞,但没有帮助。

这导致我的程序在等待服务器响应时卡住了,这种情况永远不会发生。根据我对事物的理解,InputStreamReader 似乎也在做同样的事情。

我在这里找到的关于该主题的其他问题没有回答我的问题, 如果你能回答我的问题,那就太好了。

【问题讨论】:

  • 如果服务器执行 flush() 并确保发送了换行符 (println) 将会很有趣。
  • 阻塞直到有响应和找出是否有响应之间有什么区别?无论哪种情况,您都必须在服务器上等待。

标签: java stream blocking bufferedreader inputstreamreader


【解决方案1】:

如果你想异步读取响应,我建议启动一个读取 BufferedReader 的线程。这更容易编码,也更容易控制。

【讨论】:

  • 我认为 BufferedReader 是无阻塞读取数据的最佳实践。但是对于某些设备,“readLine”甚至“ready”都会阻塞(“ready”永远不会阻塞):stackoverflow.com/questions/27427213/…
【解决方案2】:

您可能只需要 InputStream 而不将其包装在 BufferedReader

while (inputStream.available() > 0) {
     int i = inputStream.read(tmp, 0, 1024);
     if (i < 0)
          break;
     strBuff.append(new String(tmp, 0, i));
}

我希望这会有所帮助。

【讨论】:

  • 此技术不需要更改为 InputStream。
  • 这对消除阻塞行为没有任何作用
  • @EJP 如果没有输入,它不会让他停止等待吗?在他的问题中,他说“但我要做的就是首先验证服务器是否有响应。”
  • 你还没看懂我写的。 BufferedInputStream 与此代码的工作方式相同。
  • @Amir.F Tge 使用available() 的问题是人们认为它会神奇地告诉他们传入消息何时完成。它没有。这个answer的问题在于第一句话是不真实的。
【解决方案3】:

我最近使用 CountDownLatch 做了类似的事情。可能有一些更好的方法,但这很容易,而且似乎工作得相当好。您可以根据需要调整 CountDownLatch 的等待时间。

package com.whatever;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestRead {
    private static final Logger log = LoggerFactory.getLogger(TestRead.class);
    private CountDownLatch latch = new CountDownLatch(1);

    public void read() {
        URLReader urlReader = new URLReader();
        Thread listener = new Thread(urlReader);
        listener.setDaemon(true);
        listener.start();
        boolean success = false;
        try {
            success = latch.await(20000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("error", e);
        }
        log.info("success: {}", success);
    }

    class URLReader implements Runnable {
        public void run() {
            log.info("run...");
            try {
                URL oracle = new URL("http://www.oracle.com/");
                URLConnection yc = oracle.openConnection();
                BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
                String inputLine;
                while ((inputLine = in.readLine()) != null)
                    System.out.println(inputLine);
                in.close();
                latch.countDown();
            } catch (Exception ex) {
                log.error("error", ex);
            }
            log.info("consumer is done");
        }

    }
    public static void main(String[] args) {
        TestRead testRead = new TestRead();
        testRead.read();
    }
}

【讨论】:

    【解决方案4】:

    如果您使用标准 java IO,不阻塞是一项棘手的任务。常见的答案是迁移到 NIO 或 netty。 Netty 是更可取的选择。但是有时您别无选择,因此我建议您尝试我的解决方法:

    public String readResponse(InputStream inStreamFromServer, int timeout) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inStreamFromServer, Charsets.UTF_8));
        char[] buffer = new char[8092];
        boolean timeoutNotExceeded;
        StringBuilder result = new StringBuilder();
        final long startTime = System.nanoTime();
        while ((timeoutNotExceeded = (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) < timeout))) {
            if (reader.ready()) {
                int charsRead = reader.read(buffer);
                if (charsRead == -1) {
                    break;
                }
                result.append(buffer, 0, charsRead);
            } else {
                try {
                    Thread.sleep(timeout / 200);
                } catch (InterruptedException ex) {
                    LOG.error("InterruptedException ex=", ex);
                }
            }
        }
        if (!timeoutNotExceeded) throw new SocketTimeoutException("Command timeout limit was exceeded: " + timeout);
    
        return result.toString();
    }
    

    此解决方法不是灵丹妙药,但它具有一些重要功能:

    • 不使用 readline()。这种方法对网络通信很危险,因为有些服务器不返回LF/CR 符号,你的代码会卡住。当您从文件中读取时,无论如何您都会到达文件末尾,这并不重要。
    • 不使用char symbol = (char) fr.read();。这种方法比读取 char[] 慢
    • 它具有超时功能,您可能会在慢速连接时中断通信

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 2011-06-25
      • 2012-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-26
      相关资源
      最近更新 更多