我最近在我的代码库中发现了这个 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();
}
}
这是这样使用的:
线程 1
float[][] matrix;
matrix[x][y] = n;
publisher.publish();
线程 2
publisher.syncChanges();
myVar = matrix[x][y];
线程 1 是一个持续运行的后台更新线程。线程 2 是一个 HTTP 工作线程,它不关心它读取的内容是否一致或原子,只关心写入“最终”到达那里并且不会丢失作为并发神灵的祭品。
现在,这触发了我所有的警钟。自定义并发算法编写在不相关代码的深处。
不幸的是,修复代码并非易事。Java 对并发原始矩阵的支持不好。看起来解决此问题的最清晰方法是使用 a ReadWriteLock
,但这可能会对性能产生负面影响。显然,正确性更重要,但似乎我应该在将其从性能敏感区域中剥离出来之前证明这是不正确的。
根据java.util.concurrent 文档,以下创建happens-before
关系:
线程中的每个动作都发生在该线程中的每个动作之前,这些动作按程序的顺序出现在后面。
对 volatile 字段的写入发生在对同一字段的每次后续读取之前。volatile 字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。
所以听起来像:
- 矩阵写入发生在发布之前()(规则 1)
- publish() 发生在 syncChanges() 之前(规则 2)
- syncChanges() 发生在矩阵读取之前(规则 1)
所以代码确实已经为矩阵建立了一个happens-before链。
但我不相信。并发很难,而且我不是领域专家。我错过了什么?这真的安全吗?