是否需要挥发性?

Is volatile needed?

如果我有一个字节队列,它应该有一个线程生产者,另一个消费者:

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 仅被 reader 线程修改,
writeIdx 仅由 writer thread.

readIdx == writeIdx表示没有内容
而且队列最多只能占用buf.length-1字节的数据。

是否需要 volatiles 或是否可以省略它们,因为只有一个线程是一个整数状态的修饰符?

谢谢 弗兰克

如果另一个线程必须读取它,它需要是可变的。 volatile 关键字向 JVM 指示该值不能被缓存或对其更新进行重新排序,否则对其值的更新可能对其他线程不可见。

可见性问题也延伸到 buf 数组。由于 buf 需要与索引同步更改,因此写入和读取方法似乎需要同步。同步使更改可见,并确保并发调用不会导致索引和 buf 内容变得不一致。

您应该声明它们 volatile。 例如,让我们看看 readIdx。如果不是 volatile,编写器线程优化可以假设它永远不会改变,并根据该假设进行错误的优化。

但是,我没有看到您访问 readIdx 编写器线程中的任何地方(或 reader 线程中的 writeIdx),除了分配给某个局部变量 rd(或wr)。我只是假设缺少一些代码,否则你的问题真的没有意义。

Nathan 是正确的,并不是说这两个线程会覆盖彼此的变量,而是变量本身可能永远不会 visible 为另一个线程(或者更确切地说CPU-核心).

有一个有趣的队列实际上使用非易失性变量来让 CPU 更好地安排工作,LMAX disruptor