我在SO上看到了两个答案,声称Java提供的PipedInputStream
和PipedOutputStream
类是有缺陷的。但他们没有详细说明他们的错误。他们真的有缺陷,如果是这样的话?我正在编写一些使用它们的代码,所以我想知道我是否采取了错误的转向。
PipedInputStream
和PipedOutputStream
已被破坏(关于线程)。他们假设每个实例都绑定到一个特定的线程。这很奇怪。
对我而言,这似乎既不奇怪也不破碎。也许作者也有其他一些缺陷?
在实践中,最好避免使用它们。我已经在13年内使用过它们了一次,我希望我没有。
但该作者无法回想起问题所在。
与所有类一样,特别是在多个线程中使用的类,如果您滥用它们,则会遇到问题。所以我不认为PipedInputStream
可以抛出的不可预测的"write end dead" IOException
是一个缺陷(未能close()
连接的PipedOutputStream
是一个错误;请参阅文章{{3} ,Daniel Ferbers,获取更多信息)。还有其他声称的缺陷吗?
答案 0 :(得分:10)
它们没有缺陷。
与所有类一样,特别是在多个线程中使用的类,如果您滥用它们,则会遇到问题。 IOException
可以抛出的不可预测的“写死亡”PipedInputStream
不是一个缺陷(未能close()
连接的PipedOutputStream
是一个错误;请参阅文章{{3} ,丹尼尔费尔伯斯,更多信息)。
答案 1 :(得分:3)
我在我的项目中很好地使用了它们,它们对于动态修改流并传递它们非常宝贵。唯一的缺点似乎是PipedInputStream有一个短缓冲区(大约1024),我的输出流大约是8KB。
它没有任何缺陷,而且效果非常好。
-------- groovy中的例子
public class Runner{
final PipedOutputStream source = new PipedOutputStream();
PipedInputStream sink = new PipedInputStream();
public static void main(String[] args) {
new Runner().doit()
println "Finished main thread"
}
public void doit() {
sink.connect(source)
(new Producer(source)).start()
BufferedInputStream buffer = new BufferedInputStream(sink)
(new Consumer(buffer)).start()
}
}
class Producer extends Thread {
OutputStream source
Producer(OutputStream source) {
this.source=source
}
@Override
public void run() {
byte[] data = new byte[1024];
println "Running the Producer..."
FileInputStream fout = new FileInputStream("/Users/ganesh/temp/www/README")
int amount=0
while((amount=fout.read(data))>0)
{
String s = new String(data, 0, amount);
source.write(s.getBytes())
synchronized (this) {
wait(5);
}
}
source.close()
}
}
class Consumer extends Thread{
InputStream ins
Consumer(InputStream ins)
{
this.ins = ins
}
public void run()
{
println "Consumer running"
int amount;
byte[] data = new byte[1024];
while ((amount = ins.read(data)) >= 0) {
String s = new String(data, 0, amount);
println "< $s"
synchronized (this) {
wait(5);
}
}
}
}
答案 2 :(得分:1)
一个缺陷可能是作者没有明确的方式向读者表明遇到问题:
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
new Thread(() -> {
try {
writeToOut(out);
out.close();
}
catch (SomeDataProviderException e) {
// Have to notify the reading side, but how?
}
}).start();
readFromIn(in);
作者可以关闭out
,但也许读者将其误解为数据的结尾。为了正确处理此问题,需要其他逻辑。如果提供了手动断开管道的功能,将会更容易。
现在有JDK-8222924要求手动断开管道的方法。
答案 3 :(得分:0)
从我的观点来看,存在一个缺陷。更确切地说,如果应该将数据泵入PipedOutputStream的线程在实际将单个字节写入流之前过早死亡,则存在死锁的高风险。在这种情况下的问题是管道流的实现不能检测到破裂的管道。因此,从PipedInputStream读取的线程将在它第一次调用read()时永远等待(即死锁)。
断开的管道检测实际上依赖于第一次调用write()作为实现,而不是懒惰地初始化写入端线程,并且仅从该时间点断开管道检测将起作用。
以下代码重现了这种情况:
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.junit.Test;
public class PipeTest
{
@Test
public void test() throws IOException
{
final PipedOutputStream pout = new PipedOutputStream();
PipedInputStream pin = new PipedInputStream();
pout.connect(pin);
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
if(true)
{
throw new IOException("asd");
}
pout.write(0); // first byte which never get's written
pout.close();
}
catch(IOException e)
{
throw new RuntimeException(e);
}
}
});
t.start();
pin.read(); // wait's forever, e.g. deadlocks
}
}
答案 4 :(得分:0)
我在JDK实现中看到的缺陷是:
1)没有超时,读者或作者可以无限制地阻止。
2)对数据传输时的次优控制(应仅使用flush或循环缓冲区已满)
所以我创建了自己来解决上述问题,(通过ThreadLocal传递超时值):
使用方法:
希望它有所帮助...