sgware sgware - 5 days ago 6
Java Question

BufferedReader#readLine() hangs even though a line has been read

Updated Question (to be more clear):

Is there a way to design the InputStream below such that

BufferedReader#readLine()
will return after reading the new line character?

In the example below,
readLine()
hangs forever even though the reader has read a new line because (presumably) it is waiting for the buffer to fill up. Ideally,
readLine()
would return after reading the new line character.

I know something like what I want is possible, because when you read from
System.in
using
BufferedReader#readLine()
, it does not wait for the buffer to fill up before returning.

import java.io.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Example {

private static final class MyInputStream extends InputStream {

public final BlockingQueue<String> lines = new LinkedBlockingQueue<>();
private InputStream current = null;

@Override
public int read() throws IOException {
try {
if(current == null || current.available() == 0)
current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));
return current.read();
}
catch(InterruptedException ex) {
return -1;
}
}
}

public static void main(String[] args) throws Exception {
MyInputStream myin = new MyInputStream();
myin.lines.offer("a line\n");
BufferedReader in = new BufferedReader(new InputStreamReader(myin));
System.out.println(in.readLine());
}
}


Also, if there is a better way to send a string to an InputStream, I'm open to suggestions.

Accepted Solution:

Based on a suggestion from Sotirios Delimanolis in one of the comments on his solution, I'm just going to used a
PipedInputStream
instead. I've coupled it to a
PipedOutputStream
, and
BufferedReader#readLine()
returns immediately as long as I call
PipedOutputStream#flush()
after sending a string that contains a new line character.

Answer

After updated question, the only way to get the BufferedReader to stop reading after the new line character is to set the buffer size to 1, which completely removes the need for a BufferedReader.

You'll have to write your own implementation.


A BufferedReader reads more bytes than required. In your case, that means it will read further than the new line character. For example, with the Oracle JVM, it will attempt to read 8192 bytes. Through your inheritance hierarchy, this

System.out.println(in.readLine());

will attempt to invoke your read() method 8192 times.

The first 6 calls will return a value, one for each of the characters in your String's byte array. The next one, will see

if(current == null || current.available() == 0)
     current = new ByteArrayInputStream(lines.take().getBytes("UTF-8"));

and current.available() will return 0 since the ByteArrayInputStream has been fully consumed. It will then attempt to take from the BlockingQueue and block indefinitely.

Comments