Java中的输入和输出流管道

时间:2008-12-13 06:49:13

标签: java input multiple-inheritance

有没有人有任何关于在Java中创建Pipe对象的建议, 既是InputStream又是OutputStream,因为Java没有多重继承,而且这两个流都是抽象类而不是接口?

潜在的需求是让一个对象可以传递给需要InputStream或OutputStream的东西来管道从一个线程输出到另一个线程的输入。

7 个答案:

答案 0 :(得分:8)

似乎错过了这个问题的重点。如果我理解正确的话,你需要一个在一个线程中像InputStream一样工作的对象,在另一个线程中想要一个OutputStream来创建一个在两个线程之间进行通信的方法。

也许一个答案是使用组合而不是继承(无论如何都是推荐的练习)。使用getInputStream()和getOutputStream()方法创建一个包含彼此连接的PipedInputStream和PipedOutputStream的管道。

你不能直接将Pipe对象传递给需要流的东西,但是你可以传递它的get方法的返回值。

这对你有用吗?

答案 1 :(得分:5)

java.io.PipedOutputStream和java.io.PipedInputStream看起来是用于此场景的类。它们被设计为一起用于在线程之间管道数据。

如果你真的想要一些单个对象传递它,则需要包含其中一个并通过getter公开它们。

答案 2 :(得分:3)

我认为这是很常见的事情。看到这个问题。

Easy way to write contents of a Java InputStream to an OutputStream

答案 3 :(得分:1)

你不能创建一个从InputStreamOutputStream派生的类,因为它们不是接口,它们有通用的方法,而Java不允许多重继承(编译器没有如果您在新对象上调用InputStream.close(),请知道是否致电OutputStream.close()close()

另一个问题是缓冲区。 Java希望为数据分配一个静态缓冲区(不会改变)。这意味着当你使用`java.io.PipedXxxStream'时,除非你使用两个不同的线程,否则写入数据最终会被阻塞。

所以Apocalisp的答案是正确的:你必须写一个复制循环。

我建议您在项目中包含Apache的commons-io,其中包含许多帮助例程,仅用于此类任务(在流,文件,字符串及其所有组合之间复制数据)。

答案 4 :(得分:1)

答案 5 :(得分:0)

我必须实现一个缓慢连接到Servlet的过滤器,所以基本上我将servlet输出流包装到QueueOutputStream中,它将每个字节(在小缓冲区中)添加到队列中,然后将这些小缓冲区输出到第二个输出流,所以在某种程度上它充当输入/输出流,恕我直言这比JDK管道更好,这不会很好地扩展,基本上在标准JDK实现中有太多的上下文切换(每次读/写),阻塞队列对于单个生产者/消费者场景来说是完美的:

import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.*;

public class QueueOutputStream extends OutputStream
{
  private static final int DEFAULT_BUFFER_SIZE=1024;
  private static final byte[] END_SIGNAL=new byte[]{};

  private final BlockingQueue<byte[]> queue=new LinkedBlockingDeque<>();
  private final byte[] buffer;

  private boolean closed=false;
  private int count=0;

  public QueueOutputStream()
  {
    this(DEFAULT_BUFFER_SIZE);
  }

  public QueueOutputStream(final int bufferSize)
  {
    if(bufferSize<=0){
      throw new IllegalArgumentException("Buffer size <= 0");
    }
    this.buffer=new byte[bufferSize];
  }

  private synchronized void flushBuffer()
  {
    if(count>0){
      final byte[] copy=new byte[count];
      System.arraycopy(buffer,0,copy,0,count);
      queue.offer(copy);
      count=0;
    }
  }

  @Override
  public synchronized void write(final int b) throws IOException
  {
    if(closed){
      throw new IllegalStateException("Stream is closed");
    }
    if(count>=buffer.length){
      flushBuffer();
    }
    buffer[count++]=(byte)b;
  }

  @Override
  public synchronized void write(final byte[] b, final int off, final int len) throws IOException
  {
    super.write(b,off,len);
  }

  @Override
  public synchronized void close() throws IOException
  {
    flushBuffer();
    queue.offer(END_SIGNAL);
    closed=true;
  }

  public Future<Void> asyncSendToOutputStream(final ExecutorService executor, final OutputStream outputStream)
  {
    return executor.submit(
            new Callable<Void>()
            {
              @Override
              public Void call() throws Exception
              {
                try{
                  byte[] buffer=queue.take();
                  while(buffer!=END_SIGNAL){
                    outputStream.write(buffer);
                    buffer=queue.take();
                  }
                  outputStream.flush();
                } catch(Exception e){
                  close();
                  throw e;
                } finally{
                  outputStream.close();
                }
                return null;
              }
            }
    );
  }

答案 6 :(得分:0)

最好使用Pipe或ArrayBlockingQueue,我建议您不要使用PipedInput / OutputStream,因为它们有不好的作法,即使您在下面的链接中也看到它们已被要求弃用,因为它会引起很多问题。

https://bugs.openjdk.java.net/browse/JDK-8223048

对于BlockingQueue和Pipe,这里是一个简单的示例

管道:

Pipe pipe = Pipe.open();
Pipe.SinkChannel sinkChannel = pipe.sink();
String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    sinkChannel.write(buf);
}
Pipe.SourceChannel sourceChannel = pipe.source();
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

参考:http://tutorials.jenkov.com/java-nio/pipe.html

BlockingQueue:

//Shared class used by threads
public class Buffer {
    // ArrayBlockingQueue
    private BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(1);

    public void get() {
        // retrieve from ArrayBlockingQueue
        try {
            System.out.println("Consumer received - " + blockingQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void put(int data) {
        try {
            // putting in ArrayBlockingQueue
            blockingQueue.put(data);
            System.out.println("Producer produced - " + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) {
        // Starting two threads
        ExecutorService executorService = null;
        try {
            Buffer buffer = new Buffer();
            executorService = Executors.newFixedThreadPool(2);
            executorService.execute(new Producer(buffer));
            executorService.execute(new Consumer(buffer));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(executorService != null) {
                executorService.shutdown();
            }
        }
    }

public class Consumer implements Runnable {
    private Buffer buffer;

    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while (true) {
            try {
                buffer.get();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Producer implements Runnable {
    private Buffer buffer;

    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        while (true) {
            Random random = new Random();
            int data = random.nextInt(1000);
            buffer.put(data);
        }
    }
}

参考: https://github.com/kishanjavatrainer/ArrayBlockingQueueDemo/tree/master/ArrayBlockingQueueDemo