2012-10-30 40 views
0

因此,我将通过说我的多线程程序未能输出gzip可以成功解压缩的内容作为序言,因此可能还有其他问题。但是我注意到,单线程和多线程的每个块的压缩大小完全不同。Java-针对单线程和多线程的压缩差异

在我的单线程运行中,我有一个GZIPOutputStream(System.out,true)并设置了SYNC_FLUSH。我一直从system.in中读取数据,直到缓冲区已满。你可以看到,在有一个完整的缓冲区后,我告诉压缩器写入输出,然后我调用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

你能提供一个可以运行的例子吗?它看起来像你在线程之间共享缓冲区,这意味着内容可能非常随机。甚至更多,我不认为这是为每个部分创建一个新的GZIP的好主意。 –

+0

对于两个字符串a,b,如果gzip满足unzip(gzip(a + b))= unzip(gzip(a)+ gzip(b)),那么您可以为每个零件使用新的gzip实例。我没有从快速搜索中找到任何参考,但从逻辑的角度来看,这是有道理的。因为gzip被用于分块编码,比如http。 –

回答

0

开始的地方:

他们是非常不同的。

如果从评论我的假设成立(gzip的满足解压缩(gzip的(A + B))=解压缩(gzip的(A)+ gzip的(B))中的两个字符串A,B),则这一预期行为。

按照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部分开始。所以你的分块输出必须比你的正常输出长。

关于多线程的问题:我需要一个完整的样本,看看发生了什么。不需要

1

flush()finish()电话。 close()将完成,并且flush()调用只是将不必要的空白块添加到放气流中。既然你不需要flush(),你并不需要设置syncFlush真实的,因为它不会做任何事情。

单张大的gzip流,使一帮小的gzip流当然会产生完全不同的结果。每个gzip流的头部和尾部都有18字节的开销。通过你使用的小块,这个开销完全支配了结果。

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