2012-10-30 98 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)混合在一起。也許你應該在沒有嘗試壓縮的情況下開始,看看你是否可以分解一些輸入(真實文本,不只是一系列相同的字符),將塊發送到線程,讓線程對數據根本不做任何事情,然後正確重建原始輸入。一旦你能做到這一點,然後回來。