Java:BufferedReader的readLine方法的效率和可能的替代方案

我们正在努力减少延迟并提高用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字符就会返回,还是等到缓冲区满了?
  • 从使用BufferedReader获取套接字数据的速度是否更快?
  • 当输入字符串的大小小于Socket接收缓冲区的大小时会发生什么?
  • 当输入字符串的大小大于Socket接收缓冲区的大小时会发生什么?

我正在使用Java的IO库(慢慢地)掌握,所以任何指针都非常受欢迎。

谢谢!

inputReader.readLine()方法一旦命中\ n字符就会返回,还是等到缓冲区满了?

  • 它会在获得换行符后立即返回。

从使用BufferedReader获取套接字数据的速度是否更快?

  • BufferedReader需要复制一些数据。 您可以尝试NIO apis,它可以避免复制,但您可能需要在花费任何时间进行分析之前查看它是否真的是I / O是瓶颈。 一个更简单的快速修复是在套接字周围添加一个BufferedInputStream ,这样每次读取都不会到达套接字(不清楚InputStreamReader本身是否进行任何缓冲。)例如

    new BufferedReader(new InputStreamReader(new BufferedInputStream(in)))

当输入字符串的大小小于Socket接收缓冲区的大小时会发生什么?

  • BufferedReader将获取所有可用数据。 然后它将扫描此数据以查找换行符。 结果是后续读取可能已经具有BufferedReader中的数据。

当输入字符串的大小大于Socket接收缓冲区的大小时会发生什么?

  • bufferedReader将读取接收缓冲区中的内容,并且由于没有换行符或达到流的末尾,它将继续从套接字读取数据,直到找到EOF或换行符。 后续读取可能会阻塞,直到有更多数据可用。

总而言之,BufferedReader只在绝对必要时阻止。

BufferedReader的一个优点是它在您使用的输入方法(read,readLine等)和实际的套接字读取之间提供了一层分离(缓冲区),因此您不必担心所有例如“大多数行都在缓冲区中,但你需要读取另一个缓冲区以获得\ n”等。

您是否已完成性能测量,表明使用BufferedReader是您的应用程序的性能问题? 如果没有,我建议你首先选择一种输入方法,它提供你想要的function(基于行的输入由\ n’,从它的声音终止),并担心是否有“更快”的方法来做它只有当你发现输入法是一个瓶颈时。

如果基于行的输入真的是你所追求的,你最终会使用像BufferedReader这样的缓冲区,那么为什么要重新发明这个轮呢?

你的第一个问题的答案是肯定的,不是。 如果缓冲区已经包含行终止符,它将立即返回,但是如果它不包含终结符,那么它将尝试填充缓冲区,但不一定完全填充。 它只会读取,直到有一些新数据(至少一个字符)或EOF到达。

关于java的一个好处是库是开源的,所以如果你有JDK的完整副本,你可以自己查看源代码来回答这些类型的问题。 我使用eclipse作为我的IDE,默认情况下,如果将光标放在类名上并按F3,它会将您带到源(这是我获得上述答案的方式)。 需要注意的是标准分布,某些内部类/本机代码的源代码不可用。

对于你的第二个问题,我会说一般不会,因为BufferedReader使用的逻辑通常与重新创建任何代码以实现相同的任务相同。 唯一可能减缓BufferedReader的是内部它使用StringBuffer,它是同步的,而不是非同步的StringBuilder。

如果您知道传入数据的字符编码,则可能需要编写自己的类来执行二进制数据的读取,从而查找特定的行尾终结符。 这可以消除许多不必要的编码/解码和复制。 确保使用可重用的缓冲区实现某些东西(例如,如果需要String实例,可以想到NIO的CharBufferByteBuffer类,或者正确初始化的StringBuilder )。 确保你在缓冲区中有足够的空间,32Ki到64Ki对于当前的计算机来说没什么。

一旦您将数据放入可用容器中,您就可以使用本书中的任何技巧(多个线程,执行程序等)来有效地处理数据。 请记住,减慢当前CPU的唯一方法是点击缓存未命中 – 大型/动态数据集,虚假复制 – 或分支 – 不必要的循环, if语句以及更多,当然还有内核调用和I / O.