2015-06-30 2 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 байта данных.

Нужны ли летучие или их можно исключить, поскольку только один поток является модификатором одного целочисленного состояния?

ТНХ Frank

+2

Вам нужна еще более сильная синхронизация, обновления 'writeIdx' должны всегда происходить после обновлений' buf'. Это будет сложно без «синхронизации». –

+0

Согласен с Banthar. Играйте в нее безопасно, не используйте ярлыки. Напишите правильную синхронизацию. Не жертвуйте правильностью, чтобы получить наносекунду. – sstan

+0

Вы правы насчет модификации, но не забывайте, что чтение также происходит. –

ответ

5

Если другой поток должен прочитать его, она должна быть нестабильной. Ключевое слово volatile указывает JVM, что это значение нельзя кэшировать или изменить его обновления, иначе обновление его значения может быть недоступно для других потоков.

Проблема видимости распространяется и на массив buf. Поскольку buf необходимо поэтапно менять индексы, казалось бы, нужно синхронизировать методы записи и чтения. Синхронизация делает видимыми изменения и гарантирует, что одновременные вызовы не приведут к несогласованности индексов и содержимого buf.

+0

Вы можете использовать 'volatile' для обработки содержимого буфера.Трюк заключается в том, чтобы помнить, что чтение и запись поля «volatile» устанавливает связь между потоками между ними, поэтому, если поток A пишет байты в буфере перед обновлением изменчивого поля, а затем поток B анализирует буфер _after_, исследуя тот же самый изменчивый поле, тогда поток B должен увидеть байты, которые поток A записал в буфер. Но вы правы, «синхронизированный» - это более очевидный способ ... –

+0

@james: согласовано, можно было бы сделать так, чтобы работать над углом видимости, если OP хочет идти именно так. –

+0

Спасибо, что помогли. Поэтому я сделаю их неустойчивыми. синхронизация не требуется, так как writeIdx обновляется последним, чтение не может произойти раньше и наоборот. – fbenoit

2

Вы должны объявить их volatile. Посмотрим, например, на readIdx. Если это не volatile, оптимизация потока писем может предполагать, что он никогда не изменяется и делает неправильные оптимизации на основе этого предположения.

Однако, я не вижу, что доступ readIdx где-нибудь в писательнице нити (или writeIdx в считывающей нити) отдельно для присвоения некоторых локальных переменной rd (или wr). Я просто предполагаю, что какой-то код отсутствует, иначе ваш вопрос не имеет смысла.

+0

Да, недостающий код указан в комментариях. – fbenoit

1

Натан прав, это не то, что два потока будут писать друг за другом переменные, но что сами переменные рискуют никогда не быть видимым для другого потока (или, скорее, центрального ядра).

Существует интересная очередь, которая на самом деле использует энергонезависимые переменные, позволяющие ЦП лучше планировать работу, LMAX disruptor.