PipedInputStream / PipedOutputStream存在缺陷

时间:2012-02-28 14:35:03

标签: java multithreading io

我在SO上看到了两个答案,声称Java提供的PipedInputStreamPipedOutputStream类是有缺陷的。但他们没有详细说明他们的错误。他们真的有缺陷,如果是这样的话?我正在编写一些使用它们的代码,所以我想知道我是否采取了错误的转向。

One answer说:

  

PipedInputStreamPipedOutputStream已被破坏(关于线程)。他们假设每个实例都绑定到一个特定的线程。这很奇怪。

对我而言,这似乎既不奇怪也不破碎。也许作者也有其他一些缺陷?

Another answer说:

  

在实践中,最好避免使用它们。我已经在13年内使用过它们了一次,我希望我没有。

但该作者无法回想起问题所在。


与所有类一样,特别是在多个线程中使用的类,如果您滥用它们,则会遇到问题。所以我不认为PipedInputStream可以抛出的不可预测的"write end dead" IOException是一个缺陷(未能close()连接的PipedOutputStream是一个错误;请参阅文章{{3} ,Daniel Ferbers,获取更多信息)。还有其他声称的缺陷吗?

5 个答案:

答案 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传递超时值):

PipedOutputStream

使用方法:

PiedOutputStreamTest

希望它有所帮助...