Java-单线程和多线程的压缩差异

时间:2012-10-30 23:20:03

标签: java multithreading compression gzip

所以我先说这个,我的多线程程序无法输出gzip可以成功解压缩的东西,所以可能还有其他问题。但我注意到,单线程和多线程的每个块的压缩大小完全不同。

在我的单线程运行中,我有一个设置了SYNC_FLUSH的GZIPOutputStream(System.out,true)。我不断从system.in读取,直到我的缓冲区已满。

GZIPOutputStream compressor = new GZIPOutputStream(System.out, true);
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);
....
while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        compressor.write(buff,0,offset);
        compressor.flush();
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        compressor.write(buff,0,offset);
        compressor.finish();
    }
}
compressor.close();

正如你所看到的,在有一个完整的缓冲区之后,我告诉压缩器写入输出,然后我调用flush。确保我强制它压缩并清除任何剩余的输出,所以当它再次写入时,缓冲区中没有剩余数据。

所以它非常类似于您的原始输入开始时的长度(因此每个块都是它自己的单独流)。

所以在我的多线程程序中,我只有一堆线程,每个都有自己的GZIPOutputStream,而不是让一个GZIPOutputStream写入和刷新。所以基本上,用一个线程调用替换那个部分

List<Future<byte[]>> results = new ArrayList<Future<byte[]>>();
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);

while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        results.add(exec.submit(new workerThread(buff,offset)));
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        results.add(exec.submit(new workerThread(buff,offset)));
    }
}

我只是将缓冲区传递给它们进行压缩。我的所有主题都是

private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
private byte[] finalOut;
....
public byte[] call() {
    try{
        GZIPOutputStream compress = new GZIPOutputStream (bOut, true);
        compress.write(input,0,size);
        compress.flush();
        compress.close();
     }
     catch (IOException e)
     {
        e.printStackTrace();
        System.exit(-1);
     }
     finalOut = bOut.toByteArray();
     return finalOut;
 }

我认为我所做的一切都是给线程的压缩工作。我没有改变任何其他东西。但是,当我运行我的多线程程序并对结果进行hexdump时,我注意到每个块通常在两个程序之间有很大不同。我使用了一个小缓冲区和小输入,因此更容易阅读。

我的多线程程序出现crc错误,这意味着至少gzip会识别格式并开始解压缩。只是当它完成时,最终结果与CRC的预期结果不匹配(例如解压缩输出的大小等)。

老实说,我不确定为什么会这样。我本来期待一些更明显的错误,但这个似乎是随机的。它肯定在压缩。单线程和多线程程序之间的前几个字节(当然在标题之后)通常是相同的,所以我不认为我是乱序连接(加上executor.get()函数应该处理它)

我只是难过。我知道gzip可以解压缩连接的流。我将输入分成两半并单独输出然后将它们组合在我的单线程程序中并解压缩就好了。

为了记录,我只是在一个包含328个“A”字符的文件上尝试过,所以它不是很大。单个线程的GZIPOutputStream的hexdump是

0000000 8b1f 0008 0000 0000 0000 7472 581c 0000
0000010 0000 ffff 681a 0004 0000 ffff 21a2 02e2
0000020 0000 ff00 03ff a800 5bff 5c79 0001 0000

对于多线程,它是

0000000 8b1f 0008 0000 0000 0000 7472 19a4 22e0
0000010 1146 0000 ff00 03ff 7500 5f6c 80d1 0000
0000020 1f00 088b 0000 0000 0000 a200 e221 4622
0000030 0011 0000 ffff 0003 6c75 d15f 0080 0000
0000040 8b1f 0008 0000 0000 0000 21a2 02e2 0000
0000050 ff00 03ff 8a00 193b 5c21 0000 0000     

他们非常不同。

哇,这真的很长。对于那个很抱歉。真的很困惑和卡住了。

2 个答案:

答案 0 :(得分:1)

不需要flush()finish()次来电。 close()将完成,flush()调用只会向deflate流中添加不必要的空块。由于您不需要flush(),因此您无需将syncFlush设置为true,因为它不会执行任何操作。

制作单个大型gzip流并制作一堆小gzip流当然会产生完全不同的结果。每个gzip流的头部和尾部都有18字节的开销。使用您正在使用的小块,开销完全占据了结果。

你的线程示例中有一个很大的bug。虽然无螺纹示例压缩了328'A',但线程示例将'A和换行符(十进制10)混合在一起。也许你应该在没有尝试压缩的情况下开始,看看你是否可以分解一些输入(真实文本,而不仅仅是一系列相同的字符),将块发送到线程,让线程根本不对数据做任何事情,以及然后正确地重建原始输入。一旦你能做到,那就回来吧。

答案 1 :(得分:0)

从某个地方开始:

  

他们非常不同。

如果我对注释的假设成立(gzip满足unzip(gzip(a + b))= unzip(gzip(a)+ gzip(b))两个字符串a,b),则这是预期的行为。< / p>

根据RFC,每个gzip调用都会写一个标题。在Java

private void writeHeader() throws IOException {
    out.write(new byte[] {
                  (byte) GZIP_MAGIC,        // Magic number (short)
                  (byte)(GZIP_MAGIC >> 8),  // Magic number (short)
                  Deflater.DEFLATED,        // Compression method (CM)
                  0,                        // Flags (FLG)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Extra flags (XFLG)
                  0                         // Operating system (OS)
              });
}

GZIP_MAGIC is 8b1f:

private final static int GZIP_MAGIC = 0x8b1f;

And Deflater.DEFLATED is 8:

public static final int DEFLATED = 8;

标题将以:
开头 1f 8b 80 ...
您可以在输出中清楚地看到此部分(字节交换)。每个新的gzip部分都会重新开始标题。因此,您的分块输出的长度必须大于正常输出。

关于多线程问题:我需要一个完整的示例来查看正在发生的事情。

相关问题