我有一组计数器,只能在一个线程中更新。
如果我从另一个线程中读取这些值并且我没有用户使用volatile / atomic / synchronized这些值是如何过时的那样?
我问,因为我想知道我是否可以避免在这里使用volatile / atomic / synchronized。
我目前认为我无法对更新时间作出任何假设(所以我不得不至少使用volatile)。只是想确保我不会在这里遗漏一些东西。
答案 0 :(得分:4)
价值的陈旧程度完全取决于实施的自由裁量权 - spec不提供任何保证。您将编写依赖于特定JVM的实现细节的代码,这些代码可以通过更改内存模型或JIT如何重新排序代码来解决。规范似乎是为了给实施者提供他们想要的绳索,只要他们观察到由volatile,final,synchronized等强加的约束。
答案 1 :(得分:4)
我问,因为我想知道我是否可以避免在这里使用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
,不再允许进行优化。
底线:如果您需要共享状态,则需要同步。
答案 2 :(得分:0)
看起来我可以避免这些变量同步的唯一方法是执行以下操作(类似于Zan Lynx在评论中建议的那样):
当然,这种优化可能只是一个微小的改进,考虑到它会产生额外的复杂性,可能不值得。
答案 3 :(得分:0)
Java8有一个名为LongAdder的新类,它可以解决在字段上使用volatile的问题。但在那之前......
如果您不在计数器上使用volatile,则结果无法预测。如果你确实使用volatile,那么就存在性能问题,因为每次写入都必须保证缓存/内存的一致性。当有许多线程经常写时,这是一个巨大的性能问题。
对于对应用程序不重要的统计信息和计数器,我给用户选择volatile / atomic或none,没有默认值。到目前为止,大多数人都没有使用。