Java:BufferedReader 的 readLine 方法的效率和可能的替代方案
Posted
技术标签:
【中文标题】Java:BufferedReader 的 readLine 方法的效率和可能的替代方案【英文标题】:Java: Efficiency of the readLine method of the BufferedReader and possible alternatives 【发布时间】:2011-02-16 19:28:03 【问题描述】:我们正在努力减少延迟并提高用 Java 编写的进程的性能,该进程通过 BufferedReader 类的 readLine() 方法从套接字消费数据(xml 字符串)。数据由行尾分隔符 (\n) 分隔,每行可以是可变长度(6KBits - 32KBits)。我们的代码如下所示:
Socket sock = connection;
InputStream in = sock.getInputStream();
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in));
...
do
String input = inputReader.readLine();
// Executor call to parse the input thread in a seperate thread
while(true)
所以我有几个问题:
inputReader.readLine() 方法会在遇到 \n 字符时立即返回,还是会等到缓冲区已满? 是否有更快的数据采集速度 从插座比使用 缓冲阅读器? 当输入字符串的大小小于 Socket 的接收缓冲区大小时会发生什么? 当 输入字符串大于大小 Socket 的接收缓冲区?我正在(慢慢地)掌握 Java 的 IO 库,因此非常感谢任何指针。
谢谢!
【问题讨论】:
我猜您使用的是特定于应用程序的格式 - 换行符和空格在 XML 中通常不重要。 是的,抱歉应该更清楚。我们通过 TCP 连接使用来自外部应用程序的流数据。每个 XML 消息都由一个 \n 字符分隔。我想这与按顺序读取文件相同,其中每一行都是完整的 xml 文档。 【参考方案1】:inputReader.readLine() 方法会在遇到 \n 字符时立即返回,还是会等到缓冲区已满?
只要换行就会返回。从套接字获取数据是否比使用 BufferedReader 更快?
BufferedReader 需要对数据进行一些复制。您可以尝试使用 NIO api,它可以避免复制,但您可能希望在花任何时间之前先进行分析,看看是否真的是 I/O 是瓶颈。一个更简单的快速解决方法是在套接字周围添加一个BufferedInputStream
,这样每次读取都不会碰到套接字(不清楚 InputStreamReader 是否自己做任何缓冲。)例如
new BufferedReader(new InputStreamReader(new BufferedInputStream(in)))
当输入字符串的大小小于 Socket 的接收缓冲区大小时会发生什么?
BufferedReader 将获取所有可用的数据。然后它将扫描此数据以查找换行符。结果是后续读取可能已经在 BufferedReader 中有数据。当输入字符串的大小大于 Socket 的接收缓冲区大小时会发生什么?
bufferedReader 将读取接收缓冲区中的内容,并且由于没有换行符或到达流的末尾,它将继续从套接字读取数据,直到找到 EOF 或换行符。后续读取可能会阻塞,直到有更多数据可用。总而言之,BufferedReader 仅在绝对必要时才会阻塞。
【讨论】:
感谢您的详细解答。 不用担心。我希望您通过建议的更改获得所需的改进性能。如果没有,请尝试分析,如果仍然没有成功,您可以随时发布另一个问题,寻求提高性能的帮助 :-) 祝你好运!【参考方案2】:BufferedReader 的一个优点是它在您使用的输入方法(read、readLine 等)和实际的套接字读取之间提供了一层分隔(缓冲区),因此您不必担心关于所有情况,例如“大部分行都在缓冲区中,但是您需要读取另一个缓冲区才能获取 \n”等。
您是否进行过性能测量,表明使用 BufferedReader 对您的应用程序来说是一个性能问题?如果没有,我建议您首先选择一种提供所需功能的输入法(基于行的输入由 \n 终止,从它的声音来看),并担心是否有“更快”的方式来做到这一点只有当你发现输入法是一个瓶颈时。
如果基于行的输入确实是您所追求的,那么您最终会使用某种缓冲区,例如 BufferedReader,那么为什么要重新发明这个***呢?
【讨论】:
感谢您的回答。我们已经对应用程序进行了大量的分析,我们发现在处理微小消息时可能会有几毫秒的延迟。鉴于 BufferedReader 的 API 文档,它似乎没有任何意义!我们通过设置 TcpNoDelay 标志禁用了 Nagle 算法,并且正在寻找其他替代方案。 有趣。 BufferedReader 肯定会涉及数据的额外副本,但很难看出这可能需要几毫秒......【参考方案3】:第一个问题的答案是肯定的和否定的。如果缓冲区已经包含行终止符,它将立即返回,但是如果它不包含终止符,那么它将尝试填充缓冲区,但不一定完全填充。它只会在有一些新数据(至少一个字符)或到达 EOF 之前读取。
Java 的优点之一是这些库是开源的,因此如果您拥有 JDK 的完整副本,您可以自己查看源代码来回答这些类型的问题。我使用 eclipse 作为我的 IDE,默认情况下,如果您将光标放在类名上并按 F3,它将带您到源代码(这就是我获得上述答案的方式)。需要注意的是,标准发行版中某些内部类/本机代码的源代码不可用。
对于您的第二个问题,我一般会说不,因为 BufferedReader 使用的逻辑通常与任何代码都需要重新创建以实现相同的任务相同。唯一可能减慢 BufferedReader 的是它在内部使用了一个 StringBuffer,它是同步的,而不是未同步的 StringBuilder。
【讨论】:
【参考方案4】:如果您知道传入数据的字符编码,您可能想要编写自己的类来执行二进制数据的读取,寻找您特定的行尾终止符。这可能会消除很多不必要的编码/解码和复制。确保您使用可重用的缓冲区实现某些东西(例如 NIO 的 CharBuffer
或 ByteBuffer
类会浮现在脑海,或者如果您需要 String
实例,则正确初始化 StringBuilder
)。确保缓冲区中有足够的空间,32Ki 到 64Ki 对于当前的计算机来说是没有的。
在可用容器中获取数据后,您可以使用书中的任何技巧(多线程、执行程序等)来有效地处理数据。请记住,降低当前 CPU 速度的唯一方法是命中缓存未命中 - 大型/动态数据集、虚假复制 - 或分支 - 不必要的循环、if
语句以及更多,当然还有内核调用和 I/O。
【讨论】:
和 McAfee,McAfee 将一切减慢到爬行:(以上是关于Java:BufferedReader 的 readLine 方法的效率和可能的替代方案的主要内容,如果未能解决你的问题,请参考以下文章
Java 中 BufferedReader.readLine() 的最大行长?
java中BufferedReader的问题 一个程序中如何多次调用BufferedReader 我第二次调用的时候无法读取数据 如