Нужно ли летучее?

Если у меня есть очередь байтов, где, как ожидается, будет один производитель потока, другой потребитель:

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 означает, что нет контента.
И очередь может занимать только до 1 байта данных.

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

спасибо Фрэнк

3 ответа

Решение

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

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

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

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

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

Есть интересная очередь, которая на самом деле использует энергонезависимые переменные, чтобы процессор мог лучше планировать работу, разрушитель LMAX.

Другие вопросы по тегам