Ste*_*ker 15 java concurrency java-memory-model memory-barriers
我最近在我的代码库中找到了这个gem:
/** This class is used to "publish" changes to a non-volatile variable.
*
* Access to non-volatile and volatile variables cannot be reordered,
* so if you make changes to a non-volatile variable before calling publish,
* they are guaranteed to be visible to a thread which calls syncChanges
*
*/
private static class Publisher {
//This variable may not look like it's doing anything, but it really is.
//See the documentaion for this class.
private volatile AtomicInteger sync = new AtomicInteger(0);
void publish() {
sync.incrementAndGet();
}
/**
*
* @return the return value of this function has no meaning.
* You should not make *any* assumptions about it.
*/
int syncChanges() {
return sync.get();
}
}
Run Code Online (Sandbox Code Playgroud)
这样使用:
线程1
float[][] matrix;
matrix[x][y] = n;
publisher.publish();
Run Code Online (Sandbox Code Playgroud)
线程2
publisher.syncChanges();
myVar = matrix[x][y];
Run Code Online (Sandbox Code Playgroud)
线程1是一个连续运行的后台更新线程.线程2是一个HTTP工作线程,它不关心它所读取的内容是以任何方式一致的还是原子的,只是写入"最终"到达并且不会丢失作为并发神的产品.
现在,这会触发我所有的警告铃声.自定义并发算法深入写入无关代码.
不幸的是,修复代码并非易事.Java对并发原始矩阵的支持并不好.看起来最ReadWriteLock明确的解决方法是使用a ,但这可能会产生负面的性能影响.显而易见,正确性更为重要,但似乎我应该在将其从性能敏感区域中剥离之前证明这是不正确的.
根据java.util.concurrent文档,以下创建happens-before关系:
线程中的每个动作都发生在该线程中的每个动作之前,该动作在程序的顺序中稍后出现.
在每次后续读取同一字段之前,会发生对易失性字段的写入.易失性字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定.
所以听起来像:
所以代码确实已经建立了矩阵的先发条件链.
但我不相信.并发很难,我不是域专家.我错过了什么?这确实安全吗?
就可见性而言,您所需要的只是在任何易失性字段上进行易失性写入和读取。这会起作用
final float[][] matrix = ...;
volatile float[][] matrixV = matrix;
Run Code Online (Sandbox Code Playgroud)
线程1
matrix[x][y] = n;
matrixV = matrix; // volatile write
Run Code Online (Sandbox Code Playgroud)
线程2
float[][] m = matrixV; // volatile read
myVar = m[x][y];
or simply
myVar = matrixV[x][y];
Run Code Online (Sandbox Code Playgroud)
但这仅适用于更新一个变量。如果写入线程正在写入多个变量而读取线程正在读取它们,则读取器可能会看到不一致的图片。通常是通过读写锁来处理的。写时复制可能适合某些使用模式。
Doug Lea 有一个新的“StampedLock” http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/StampedLock.html for Java8,这是读写锁的一个版本,读取成本要便宜得多锁。但使用起来也困难得多。基本上,读者获取当前版本,然后继续读取一堆变量,然后再次检查版本;如果版本没有更改,则读取会话期间没有并发写入。