我有两个主题。其中一个写入PipedOutputStream,另一个从相应的PipedInputStream读取。背景是一个线程正在从远程服务器下载一些数据,并通过管道流将其多路复用到其他几个线程。
问题是有时候(特别是在下载大型(> 50Mb)文件时)我在尝试从PipedInputStream读取时遇到 java.io.IOException:管道坏了 。
Javadoc说A pipe is said to be broken if a thread that was providing data bytes to the connected piped output stream is no longer alive.
确实,我的写作线程在将所有数据写入PipedOutputStream之后真的死了。
任何解决方案?如何防止PipedInputStream抛出此异常?我希望能够读取写入PipedOutputStream的所有数据,即使编写线程完成了他的工作。 (如果有人知道如何在读取所有数据之前保持线程处于活动状态,则此解决方案也是可接受的)。
答案 0 :(得分:18)
使用java.util.concurrent.CountDownLatch,并且在第二个线程发出信号已完成从管道读取之前,不要结束第一个线程。
更新:快速而脏的代码,以说明我在下面的评论
final PipedInputStream pin = getInputStream();
final PipedOutputStream pout = getOutputStream();
final CountDownLatch latch = new CountDownLatch(1);
InputStream in = new InputStream() {
@Override
public int read() throws IOException {
return pin.read();
}
@Override
public void close() throws IOException {
super.close();
latch.countDown();
}
};
OutputStream out = new OutputStream(){
@Override
public void write(int b) throws IOException {
pout.write(b);
}
@Override
public void close() throws IOException {
while(latch.getCount()!=0) {
try {
latch.await();
} catch (InterruptedException e) {
//too bad
}
}
super.close();
}
};
//give the streams to your threads, they don't know a latch ever existed
threadOne.feed(in);
threadTwo.feed(out);
答案 1 :(得分:7)
当您使用它的线程结束时,您是否正在关闭PipedOutputStream
?您需要这样做,以便将其中的字节刷新到相应的PipedInputStream
。
答案 2 :(得分:1)
PipedInputStream
和PipedOutputStream
已被破坏(关于线程)。他们假设每个实例都绑定到一个特定的线程。这很奇怪。我建议使用您自己的(或至少是不同的)实现。
答案 3 :(得分:0)
当使用多个读取器或写入器线程时,您可能会遇到这些类的问题(请参见JDK-4028322)。
但是,大多数用户可能只使用一个读取器和一个写入器线程。既然您也遇到这种情况,那么写完后很可能没有close
PipedOutputStream
遇到管道破损的原因。
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
// You can of course also use an Executor or similar for this
new Thread(() -> {
// Your method writing the data
writeDataTo(out);
// Close PipedOutputStream so that reader knows that the end of the data
// has been reached
try {
out.close();
}
catch (IOException e) {
// Handle exception or simply ignore it; current implementation will NEVER
// throw an IOException: https://github.com/openjdk/jdk/blob/0e14d5a98453407488057e6714f90f04d7dfa383/src/java.base/share/classes/java/io/PipedOutputStream.java#L174
}
}).start();
// Your method reading the data
readDataFrom(in);
// Close PipedInputStream so that writer fails instead of blocking infinitely in case
// it tries to write again (for whatever reason)
in.close();
也不需要手动调用PipedOutputStream.flush()
,它只会通知正在等待的读者,但如果直接调用close()
,则不会丢失任何数据。
遗憾的是,目前关于所有这些信息尚不清楚。通常,依靠实现不是一个好主意,但是在这种情况下,它可能是唯一明智的解决方案: