java - 警惕 volatile /同步性能损失

标签 java multithreading volatile

考虑 Java 中的一个统计类,用于计算成功和失败。

public class Stat {
  long successes=0, failures=0;
  public success() {successes += 1;}
  public failed() {failures += 1;}
  public logStats() { ... read the values and log them ... }
}

应从另一个线程定期调用 logStats() 方法来记录当前的统计计数器。那么这段代码是错误的,因为记录器线程可能看不到最新的值,因为没有任何东西是同步的、 volatile 的或原子的。

因为我使用的是long,甚至volatile也不够。甚至这个makes the increment more expensive than without 。假设计数的频率非常高,而日志每分钟只运行一次,有没有办法在进入 logStats() 时强制将新值分发到所有线程? logStats() 同步是否有效。有点像半边同步。我知道书上说不要这样做。我只是想了解在这种特定设置下它是否有效以及为什么。

此外,我应该注意,只有一个线程进行计数。

编辑请仔细阅读问题是什么。我不是问如何以不同的方式实现这一点。我问是否以及为什么存在一些半边一致性强制,其中写入线程不关心,但读取线程主动强制查看最新值。我的预感是它可能只适用于一个synchronize,但我还无法解释为什么或为什么不。

最佳答案

Java 没有提供实现“半边”并发的方法,我什至怀疑硬件是否可以实现。

但是,您可能想知道同步保证对您来说有多重要。确实,语言并不能保证这一点,但只要您在 64 位平台上运行,long 访问肯定会是“原子的”,因为您的程序即使既不是 volatile 也不是同步,也不会在两个 32 位读取周期中执行这些操作。这不像你在同步其他任何东西,所以我怀疑你的记录器获取真正的“最新”值,而不是一些几百个周期前写入的值,对你来说真的很重要。

另外,请注意,如果此代码确实对性能至关重要,那么即使完全非同步访问也不是免费的。一旦您在处理器之间共享缓存行,相关 CPU 之间就会出现缓存同步流量,虽然我没有对其进行基准测试或任何其他操作,但我怀疑将 volatile 添加到这个方程不会产生太大的影响。与其他 CPU 通信以更改缓存行状态的延迟可能比避免一个 CPU 指令流中的内存障碍更大。

为了避免任何这些惩罚,您可能需要做一些事情,例如拥有一个与其他线程共享统计信息的类,与“真实”计数器分开,您只更新一次,例如 10,000 次更新真正的柜台。这样,您还可以对该共享类进行 volatile 访问,而不会给编写器线程带来任何常规惩罚。

关于java - 警惕 volatile /同步性能损失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36036771/

相关文章:

java - android服务在应用程序被杀死时自行重启

c++ - std::async 有什么问题?

c++ - 如果 volatile 是不必要的,为什么 std::atomic 方法提供 volatile 重载?

java - System.out.println 与 java volatile

java - IntelliJ- "SnapShooter listening on port XXXXX"?

java - JDBC4中的驱动类是如何定位的

java - 子类不调用重写父接口(interface)方法

python - 在进行网络编程时,是否有经验法则来确定使用多少个线程?

Lua中的多线程

c++ - 编译器有时可以缓存声明为 volatile 的变量吗