是否需要挥发性?
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。
如果我有一个字节队列,它应该有一个线程生产者,另一个消费者:
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。