我有一组计数器,它们只会在一个线程中更新。
如果我从另一个线程读取这些值并且我不使用 volatile/atomic/synchronized 这些值会过时吗?
我问我是否可以避免在这里使用 volatile/atomic/synchronized。
我目前认为我无法对更新时间做出任何假设(因此我被迫至少使用 volatile)。只是想确保我在这里没有遗漏任何东西。
我有一组计数器,它们只会在一个线程中更新。
如果我从另一个线程读取这些值并且我不使用 volatile/atomic/synchronized 这些值会过时吗?
我问我是否可以避免在这里使用 volatile/atomic/synchronized。
我目前认为我无法对更新时间做出任何假设(因此我被迫至少使用 volatile)。只是想确保我在这里没有遗漏任何东西。
我问我是否可以避免在这里使用 volatile/atomic/synchronized。
在实践中,CPU 缓存可能会定期同步到主内存(多久取决于许多参数),所以听起来你会不时看到一些新值。
但这没有抓住重点:实际问题是,如果您不使用正确的同步模式,编译器可以自由地“优化”您的代码并删除更新部分。
例如:
class Broken {
boolean stop = false;
void broken() throws Exception {
while (!stop) {
Thread.sleep(100);
}
}
}
编译器被授权将该代码重写为:
void broken() throws Exception {
while (true) {
Thread.sleep(100);
}
}
stop
因为没有义务在您执行该broken
方法时检查非易失性是否可能发生变化。stop
将变量标记为volatile
,不再允许优化。
底线:如果您需要共享状态,则需要同步。
值的陈旧程度完全取决于实现的自由裁量权——规范不提供任何保证。您将编写的代码取决于特定 JVM 的实现细节,并且可能会因内存模型的更改或 JIT 对代码的重新排序方式的更改而被破坏。编写规范的目的似乎是为实现者提供尽可能多的绳索,只要他们遵守 volatile、final、synchronized 等施加的约束。
Java8 有一个名为 LongAdder 的新类,它有助于解决在字段上使用 volatile 的问题。但在那之前...
如果您不在计数器上使用 volatile,那么结果是不可预测的。如果你确实使用了 volatile ,那么就会出现性能问题,因为每次写入都必须保证缓存/内存的一致性。当有很多线程频繁写入时,这是一个巨大的性能问题。
对于对应用程序不重要的统计信息和计数器,我为用户提供了 volatile/atomic 或 none 选项,默认为 none。到目前为止,大多数都没有使用。
看起来我可以避免这些变量同步的唯一方法是执行以下操作(类似于 Zan Lynx 在评论中建议的):
当然,这种优化可能只是边际改进,考虑到它会产生额外的复杂性,可能不值得。