2015-06-30 84 views
4

如果我有一個字節隊列,它預計將有一個線程生產者,另一消費者:是否需要變動?

class ByteQueue{ 
    byte[] buf; 
    /*volatile?*/ int readIdx; 
    /*volatile?*/ int writeIdx; 
    Runnable writeListener; 
    Runnable readListener; 
    // ... 
    void write(byte[] b){ 
     int wr = writeIdx; 
     int rd = readIdx; 
     // check consistency and free space using wr+rd 
     // copy to buf, starting at wr, eventually wrap around 
     // update writeIdx afterwards 
     writeIdx = (wr + b.length) % buf.length; 
     // callback to notify consumer for data available 
     writeListener.run(); 
    } 
    void read(byte[] b){ 
     int wr = writeIdx; 
     int rd = readIdx; 
     // check consistency and available data using wr+rd 
     // copy buf to b, starting at rd, eventually wrap around 
     // update readIdx afterwards 
     readIdx = (rd + b.length) % buf.length; 
     // callback to notify producer for free space available 
     readListener.run(); 
    } 
    int available() { return (writeIdx - readIdx) % buf.length; } 
    int free() { return buf.length - available() -1; } 
    // ... 
} 

這種類型的隊列不應該需要同步。
readIdx僅由讀者線程修改,
writeIdx只能由寫入器線程修改。

readIdx == writeIdx表示沒有內容。
而隊列只能佔用buf.length-1字節的數據。

揮發物是否需要或可以省略,因爲只有一個線程是一個整數狀態的修飾符?

THX 弗蘭克

+2

您需要更強大的同步,'writeIdx'更新必須始終在'buf'更新後發生。沒有'synchronized'就會很棘手。 –

+0

同意Banthar。安全地玩,不要走捷徑。編寫適當的同步。不要爲了獲得納秒而犧牲正確性。 – sstan

+0

你對修改是正確的,但不要忘記讀取也正在發生。 –

回答

5

如果另一個線程來讀它,它需要揮發。關鍵字volatile向JVM指示該值無法被緩存或者其更新被重新排序,否則對其值的更新可能對其他線程不可見。

可見性問題也擴展到了buf數組。由於buf需要與索引同步更改,因此需要同步寫入和讀取方法。同步使更改可見,並確保併發調用不會導致索引和buf內容變得不一致。

+0

您也可以使用'volatile'來處理緩衝區的內容。關鍵是要記住,讀寫'volatile'字段會在線程之間建立一個before-before關係,因此如果線程A在更新易失性字段之前在緩衝區中寫入字節,然後線程B檢查緩衝區_after_檢查相同的volatile字段,那麼線程B應該看到線程A寫入緩衝區的字節。但你說的對,'同步'是更明顯的方法...... –

+0

@詹姆斯:同意,如果OP想要這樣做,可以讓搭載可視角度工作。 –

+0

謝謝,幫助。所以我會讓他們變得不穩定。不需要同步,因爲writeIdx是最後更新的,讀取之前不會發生,反之亦然。 – fbenoit

2

你應該聲明他們volatile。 讓我們看看,例如,在readIdx。如果它不是volatile,編寫器線程優化可以認爲它從不改變,並根據該假設做出錯誤的優化。

不過,我沒有看到你訪問readIdx隨時隨地寫線程(或writeIdx在讀線程)除用於分配給一些局部變量rd(或wr)。我只是假設有一些代碼缺失或者你的問題沒有意義。

+0

是在評論中提到了缺少的代碼。 – fbenoit

1

Nathan是正確的,這並不是說兩個線程會覆蓋其他所有變量,但是變量本身有可能會冒另一個線程(或CPU核心)的可見的風險。

有一個有趣的隊列實際上使用非易失性變量來讓CPU更好地安排工作,LMAX disruptor