需要挥发性吗?

时间:2015-06-30 18:55:35

标签: java multithreading volatile memory-visibility

如果我有一个字节队列,那里应该有一个线程生成器,另一个消费者:

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 弗兰克

3 个答案:

答案 0 :(得分:5)

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

可见性问题也延伸到buf数组。由于buf需要随索引一步改变,因此看起来需要同步write和read方法。同步使更改可见,并确保并发调用不会导致索引和buf内容变得不一致。

答案 1 :(得分:2)

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

但是,我没有看到您在编写器线程(或读者线程中的readIdx)中的任何位置访问writeIdx除了分配给某个局部变量rd(或{ {1}})。我只是假设有一些代码缺失或者你的问题确实没有用。

答案 2 :(得分:1)

Nathan是正确的,并不是两个线程会写出其他变量,而是变量本身会冒险从来没有可见用于另一个线程(或者更确切地说是CPU核心)。

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