缓冲后台InputStream实现

时间:2010-01-28 10:54:48

标签: java multithreading compression inputstream producer-consumer

我编写了后台InputStream(和OutputStream)实现,它们包含其他流,并在后台线程上预读,主要是允许解压缩/压缩在不同的线程中发生解压缩的流。

这是一个相当标准的生产者/消费者模型。

这似乎是一种简单的方法,可以通过简单的流程读取,处理和写入数据来充分利用多核CPU,从而更有效地利用CPU和磁盘资源。也许“高效”不是最好的词,但与直接从ZipInputStream读取并直接写入ZipOutputStream相比,它提供了更高的利用率,对我来说更感兴趣,减少了运行时间。 / p>

我很高兴发布代码,但我的问题是我是否正在重新发明现有(并且运算量更大)库中现有的东西?

修改 - 发布代码......

BackgroundInputStream的代码低于(BackgroundOutputStream非常相似),但有些方面我想改进。

  1. 看起来我的工作太过艰难,无法前后传递缓冲区。
  2. 如果调用代码抛弃对BackgroundInputStream的引用,backgroundReaderThread将永远存在。
  3. 信号eof需要改进。
  4. 异常应该传播到前台线程。
  5. 我想允许使用提供的Executor中的帖子。
  6. close()方法应该通知后台线程,并且不应该关闭包装的流,因为包装的流应该由从中读取的后台线程拥有。
  7. 应该适当地照顾关闭后阅读等愚蠢的事情。

  8. package nz.co.datacute.io;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Arrays;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public class BackgroundInputStream extends InputStream {
        private static final int DEFAULT_QUEUE_SIZE = 1;
        private static final int DEFAULT_BUFFER_SIZE = 64*1024;
        private final int queueSize;
        private final int bufferSize;
        private volatile boolean eof = false;
        private LinkedBlockingQueue<byte[]> bufferQueue;
        private final InputStream wrappedInputStream;
        private byte[] currentBuffer;
        private volatile byte[] freeBuffer;
        private int pos;
    
        public BackgroundInputStream(InputStream wrappedInputStream) {
            this(wrappedInputStream, DEFAULT_QUEUE_SIZE, DEFAULT_BUFFER_SIZE);
        }
    
        public BackgroundInputStream(InputStream wrappedInputStream,int queueSize,int bufferSize) {
            this.wrappedInputStream = wrappedInputStream;
            this.queueSize = queueSize;
            this.bufferSize = bufferSize;
        }
    
        @Override
        public int read() throws IOException {
            if (bufferQueue == null) {
                bufferQueue = new LinkedBlockingQueue<byte[]>(queueSize);
                BackgroundReader backgroundReader = new BackgroundReader();
                Thread backgroundReaderThread = new Thread(backgroundReader, "Background InputStream");
                backgroundReaderThread.start();
            }
            if (currentBuffer == null) {
                try {
                    if ((!eof) || (bufferQueue.size() > 0)) {
                        currentBuffer = bufferQueue.take();
                        pos = 0;
                    } else {
                        return -1;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int b = currentBuffer[pos++];
            if (pos == currentBuffer.length) {
                freeBuffer = currentBuffer;
                currentBuffer = null;
            }
            return b;
        }
    
        @Override
        public int available() throws IOException {
            if (currentBuffer == null) return 0;
            return currentBuffer.length;
        }
    
        @Override
        public void close() throws IOException {
            wrappedInputStream.close();
            currentBuffer = null;
            freeBuffer = null;
        }
    
        class BackgroundReader implements Runnable {
    
            @Override
            public void run() {
                try {
                    while (!eof) {
                        byte[] newBuffer;
                        if (freeBuffer != null) {
                            newBuffer = freeBuffer;
                            freeBuffer = null;
                        } else {
                            newBuffer = new byte[bufferSize];
                        }
                        int bytesRead = 0;
                        int writtenToBuffer = 0;
                        while (((bytesRead = wrappedInputStream.read(newBuffer, writtenToBuffer, bufferSize - writtenToBuffer)) != -1) && (writtenToBuffer < bufferSize)) {
                            writtenToBuffer += bytesRead;
                        }
                        if (writtenToBuffer > 0) {
                            if (writtenToBuffer < bufferSize) {
                                newBuffer = Arrays.copyOf(newBuffer, writtenToBuffer);
                            }
                            bufferQueue.put(newBuffer);
                        }
                        if (bytesRead == -1) {
                            eof = true;
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    

2 个答案:

答案 0 :(得分:3)

听起来很有趣。我从来没有遇到任何开箱即用的事情,但如果可用,尝试使用空闲核心进行压缩是非常有意义的。

也许你可以使用Commons I/O - 它是一个经过良好测试的lib,它可以帮助处理一些更无聊的东西,让你专注于扩展很酷的并行部分。也许您甚至可以将您的代码贡献给Commons项目; - )

答案 1 :(得分:0)

我会感兴趣。我已经考虑过一个类似的项目,但无法弄清楚如何处理无法完成压缩的碎片。