Java PipedInputStream/PipedOutStream存在缺陷
我在上看到了两个答案,声称Java提供的Java PipedInputStream/PipedOutStream存在缺陷,java,multithreading,io,Java,Multithreading,Io,我在上看到了两个答案,声称Java提供的PipedInputStream和PipedOutputStream类存在缺陷。但他们没有详细说明他们的问题所在。他们真的有缺陷吗?如果有,是以什么方式?我目前正在编写一些使用它们的代码,所以我想知道我是否走错了方向 他说: PipedInputStream和PipedOutputStream已断开(关于线程)。他们假设每个实例都绑定到一个特定的线程。这很奇怪 在我看来,这既不奇怪也不坏。也许作者还有其他一些缺陷 他说: 在实践中,最好避免这种情况。我13
PipedInputStream
和PipedOutputStream
类存在缺陷。但他们没有详细说明他们的问题所在。他们真的有缺陷吗?如果有,是以什么方式?我目前正在编写一些使用它们的代码,所以我想知道我是否走错了方向
他说:
PipedInputStream
和PipedOutputStream
已断开(关于线程)。他们假设每个实例都绑定到一个特定的线程。这很奇怪
在我看来,这既不奇怪也不坏。也许作者还有其他一些缺陷
他说:
在实践中,最好避免这种情况。我13年用过一次,我希望我没有用过
但这位作者记不起问题是什么
与所有类一样,尤其是在多线程中使用的类,如果误用它们,您将遇到问题。因此,我不认为<代码> PIPEDInvestStudio可能是一个缺陷(未能<代码>关闭())/代码>连接的<代码> PiPodoOutsStudio<<代码>是一个bug;请参阅Daniel Ferbers的文章以获得更多信息。还有哪些声称的缺陷?它们没有缺陷
与所有类一样,尤其是在多线程中使用的类,如果误用它们,您将遇到问题。
PipedInputStream
可能引发的不可预知的“write end dead”IOException
不是一个缺陷(未能close()
连接的PipedOutputStream
是一个缺陷;有关更多信息,请参阅Daniel Ferbers的文章).我在我的项目中很好地使用了它们,它们对于动态地修改流并传递流是非常宝贵的。唯一的缺点似乎是PipedInputStream有一个很短的缓冲区(大约1024个),而我的outputstream的速度大约为8KBs
它没有缺点,而且工作得很好
--------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()
}
}
类使用者扩展线程{
输入流输入
消费者(输入流输入)
{
this.ins=ins
}
公开募捐
{
println“用户运行”
整数金额;
字节[]数据=新字节[1024];
而((金额=ins.read(数据))>=0){
字符串s=新字符串(数据,0,金额);
println“<$s”
已同步(此){
等待(5);
}
}
}
}在我看来,有一个缺陷。更准确地说,如果应该将数据泵入PipeDoutpStream的线程在实际将单个字节写入流之前过早死亡,则存在死锁的高风险。这种情况下的问题是,管道流的实现无法检测到破裂的管道。因此,从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
}
}
我在JDK实现中看到的缺陷有: 1) 没有超时,读写器可以无限阻塞 2) 对数据传输时间的次优控制(应仅在刷新或循环缓冲区已满时进行) 因此,我创建了自己的解决方案(通过ThreadLocal传递的超时值): 如何使用:
希望它能有所帮助……一个缺陷可能是编写者没有明确的方式向读者表明它遇到了问题:
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);
作者可以将关闭
,但读者可能会将其误解为数据的结束。为了正确处理这个问题,需要额外的逻辑。如果提供了手动断开管道的功能,则会更容易
现在有一种方法要求手动断开管道。这种方法可以覆盖它。它们并不是真的“有缺陷”,只是有点棘手,而且通常还有一种符号代码的味道,如果你100%确定你需要它们,并且在设计中没有错误,那么使用它们就没有真正的问题……我想用一个,快速看一下。这至少是“功能不足”,因为读取线程不会真正等待写入线程写入完整的读取请求,如果写入程序关闭它,则会以EOF异常中止。它具有非常原始的线程处理和同步,并要求缓冲区与最大的读取请求一样大。我想听听您对我最近遇到的一个问题的看法。你能看一下吗?您希望写入和读取操作在独立线程中。要使使用者不会因为竞争条件而立即退出,最好在执行读取操作之前先执行pin.available()检查,查看其是否大于0。然后可以重复执行可用和读取操作,直到可用==0为止。您还希望在写入程序断开连接的情况下对读取端进行异常处理。当出现异常时,此测试代码无法关闭输出流。如果用
try(OutputStream pout\u closed=pout){…}
包围try块的内容,则代码不会死锁。@RobertCasey您仍然需要等待available()
return>0,这与直接调用read()
一样重要。此外,当读取线程在read()
调用上已被阻塞时,您实际上无法执行太多异常处理。@AlexQ关闭管道将向读取器指示数据已到达末尾。它可能会错误地处理这种情况,您需要额外的逻辑来确定是否实际到达了端点或写入程序遇到了问题。sink.close()丢失了?因为Java 6,您可以为PipedInputStream指定自定义管道大小
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);