2012-02-28 14 views
8

He visto dos respuestas en SO que afirman que las clases PipedInputStream y PipedOutputStream provistas por Java son defectuosas. Pero no dieron más detalles sobre lo que estaba mal con ellos. ¿Son realmente defectuosos, y si es así de qué manera? Actualmente estoy escribiendo un código que los usa, por lo que me gustaría saber si estoy tomando un giro equivocado.Defectos con PipedInputStream/PipedOutputStream

One answer dijo:

PipedInputStream y PipedOutputStream están rotos (con respecto a enhebrar). Suponen que cada instancia está vinculada a un hilo en particular. Esto es extraño

Para mí eso no parece ni bizarro ni roto. Quizás el autor también tenía algunos otros defectos en mente?

Another answer dijo:

En la práctica, es mejor evitarlos. Los he usado una vez en 13 años y desearía no haberlo hecho.

Pero ese autor no pudo recordar cuál era el problema.


Al igual que con todas las clases, y especialmente a las clases utilizadas en varios subprocesos, que tendrán problemas si se les hace mal uso. Por lo tanto, no considero el impredecible "write end dead" IOException que PipedInputStream puede arrojar como un defecto (a falta de close() el PipedOutputStream conectado es un error; consulte el artículo Whats this? IOException: Write end dead, de Daniel Ferbers, para obtener más información). ¿Qué otros defectos reclamados hay?

+0

Este http://stackoverflow.com/questions/484119/why-doesnt-more-java-code-use-pipedinputstream-pipedoutputstream tipo de lo cubre. En realidad no son "defectuosos", son un poco engañosos y generalmente tienen un olor a código de señal, si estás 100% seguro de que los necesitas y de que no hay ningún error en el diseño, no hay un problema real con su uso ... – TC1

+1

Una mirada rápida ya que quería usar una. Está al menos "Subrayado" ya que el hilo de lectura realmente no espera que el hilo de escritura escriba la solicitud de lectura completa, y aborta con excepción de EOF si el escritor lo cierra, etc. Tiene un manejo y sincronización de hilos muy primitivos, y requiere que el buffer sea tan grande como la solicitud de lectura más grande. – peterk

Respuesta

10

No son defectuosos.

Al igual que con todas las clases, y especialmente las clases utilizadas en múltiples hilos, tendrá problemas si los usa mal. El impredecible "escribir final muerto" IOException que PipedInputStream puede arrojar no es un defecto (si falla close(), el PipedOutputStream conectado es un error; consulte el artículo Whats this? IOException: Write end dead, por Daniel Ferbers, para obtener más información).

+0

Me gustaría su opinión sobre un problema que tuve recientemente. ¿Podría echar un vistazo? http://stackoverflow.com/questions/21884188/android-pipedinputstream-messes-up-data – fabian

3

Los he usado muy bien en mi proyecto y son invaluables para modificar flujos sobre la marcha y pasarlos. El único inconveniente parece ser que PipedInputStream tenía un corto buffer (alrededor de 1024) y mi outputtream estaba bombeando alrededor de 8KB.

No tiene ningún defecto y funciona perfectamente.

Ejemplo -------- en el maravilloso

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

} 

}

+0

sink.close() se ha perdido? – zeugor

-2

Desde mi punto de vista hay un defecto. Más precisamente, existe un alto riesgo de un punto muerto si el subproceso que debe bombear datos en el PipedOutputStream muere prematuramente antes de que realmente escriba un solo byte en la secuencia. El problema en tal situación es que la implementación de las transmisiones por cañerías no es capaz de detectar la tubería rota. En consecuencia, la lectura de hilo de PipedInputStream esperará por siempre (es decirpunto muerto) en su primera llamada para leer().

La detección de tuberías rotas depende en realidad de la primera llamada a write() ya que la implementación inicializa la rosca del lado de escritura y solo a partir de ese momento la detección de tuberías rotas funcionará.

El código siguiente reproduce la situación:

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 
    } 
} 
+0

Desea que sus operaciones de escritura y lectura estén en subprocesos independientes. Para que el consumidor no salga de inmediato debido a una condición de carrera, es mejor hacer una prueba de pin.available() para ver si es mayor que 0 antes de realizar una operación de lectura. Luego puede hacer repetidas operaciones de lectura y lectura hasta que esté disponible == 0 nuevamente. También desea tener control de excepciones para el lado de lectura en caso de que el escritor se desconecte. –

+0

Este código de prueba no cierra la secuencia de salida cuando hay una excepción. Si rodea el contenido de su bloque try con 'try (OutputStream pout_closed = pout) {...}' el código no se estancará. –

0

Los defectos que veo con la implementación JDK son:

1) No hay tiempos de espera, lector o escritor puede bloquear infinitamente.

2) control subóptimo termina cuando se transfieren datos (se debe hacer solamente con ras, o cuando buffer circular está llena)

así que creé mi propia para abordar el (valor por encima, tiempo de espera pasó a través de un ThreadLocal) :

PipedOutputStream

Modo de empleo:

PiedOutputStreamTest

Espero que ayude ...

Cuestiones relacionadas