BufferedReader#readLine()虽然已经读取了一行,但仍然挂起

3

更新问题(以更清晰):

有没有方法设计下面的输入流,使得BufferedReader#readLine()在读取新行字符后立即返回?

在下面的示例中,即使读者已经读取了新行,readLine()会永远挂起,因为(大概是)它正在等待缓冲区填满。理想情况下,readLine()将在读取新行字符后立即返回。

我知道像我想要的这样的东西是可能的,因为当您使用BufferedReader#readLine()System.in读取时,它不会等待缓冲区填满才返回。

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());
    }
}

此外,如果有更好的方法将字符串发送到InputStream中,我很乐意听取建议。
接受的解决方案:
基于Sotirios Delimanolis在他的解决方案评论中的建议,我只是使用了PipedInputStream。我将其与PipedOutputStream配对,并且只要在发送包含换行符的字符串后调用PipedOutputStream#flush(),BufferedReader#readLine()就会立即返回。

我已经检查过确保字符被读取,实际上#read()被调用了7次,首先是"a",然后是空格,然后是"l"……一直到换行符。所以我知道InputStreamReader获取了所有内容,但由于某种原因,BufferedReader没有意识到已经读取了完整的一行。 - Stephen Ware
2个回答

3

更新问题后,唯一的停止读取 BufferedReader 直到换行符的方法是将缓冲区大小设置为 1,这完全消除了对 BufferedReader 的需求。

你需要编写自己的实现。


BufferedReader 会读取比所需更多的字节。在你的情况下,这意味着它将继续读取超出换行符的内容。例如,在 Oracle JVM 中,它将尝试读取 8192 字节。通过你的继承层次结构,这个

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

该方法将尝试调用您的read()方法8192次。

前6次调用将返回一个值,分别对应于您的String字节数组中的每个字符。接下来的一次调用将会看到

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

current.available() 将返回 0,因为 ByteArrayInputStream 已经被完全消耗。然后它将尝试从 BlockingQueue 中获取并无限期阻塞。


0
此外,如果有更好的方法将字符串发送到InputStream,我愿意听取建议。
嗯,您可以尝试使用BufferedReader而不是InputStream,代码类似于以下内容:
public int read(String directory) throws Exception{
    String line = "";
    File file = new File(directory);
    FileReader fr = new FileReader(file);
    BufferedReader br = new BufferedReader(fr);

    do{
        lines.add(br.readLine());
    while(br.readLine() != null);

    br.close();

    return Integer.parseInt(line);
}

在使用do/while时,要小心执行lines.add(br.readLine());,因为第一个readLine()可能会返回null。 - Greg
我面临的任务是将字符串发送到InputStream,因此使用InputStream是不可避免的。然而,我愿意考虑其他更好的方法来将字符串发送到InputStream。 - Stephen Ware
也许可以使用 ObjectInputStream? - hsirkar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接