8

假设以下两个计数器实现:

class Counter {
  private final AtomicInteger atomic = new AtomicInteger(0);
  private int i = 0;

  public void incrementAtomic() {
    atomic.incrementAndGet();
  }

  public synchronized void increment() {
    i++;
  }
}

乍一看,原子应该更快,更具可扩展性。他们是,我相信。但是它们总是比synchronized块快吗?或者当这个规则被打破时存在一些情况(例如SMP/单CPU机器,不同的CPU ISA,操作系统等)?

4

5 回答 5

5

incrementAndGet可以很好地实现为 CAS 循环。在高度满足的情况下,可能导致n -1 个线程失败,从而导致 O( n ) 问题,对于n 个线程。

(对于@Geek:

通常getAndIncrement可以实现如下:

 int old;
 do {
     old = value;
 } while (!compareAndSet(value, old, old+1));
 return old;

想象一下,您有n 个线程在同一个原子上执行此代码,并且它们恰好彼此同步。第一次迭代确实有效。只有一个 CAS 会成功。其他n -1 个线程将重复该练习,直到只剩下一个。所以总工作量是 O( n ^2)(最坏情况)而不是 O( n )。)

话虽如此,获取锁最多需要做类似的事情,而锁在激烈竞争时并不是最好的。在使用 CAS 循环之前,您不太可能看到锁有太多优势,这需要在 get 和 compareAndSwap 之前进行大量计算。

于 2012-07-19T00:48:30.167 回答
3

或者当这个规则被打破时存在一些情况(例如SMP/单CPU机器,不同的CPU ISA,操作系统等)?

我一个都不知道。(我随时准备被纠正......如果有人知道一个具体的反例。)

但是(这是我的主要观点)没有理论上的理由为什么您不能拥有与类型synchronized相同或更快的硬件架构或实现不佳的 JVM atomic。(这两种同步形式的相对速度是一个实现问题,因此只能针对现有实现进行量化。)

当然,这并不意味着你永远不应该使用synchronized. 该synchronized构造有许多原子类没有解决的用例。

于 2012-07-19T00:00:34.167 回答
2

它依赖于实现——所以最终你需要在你的特定平台/JVM/配置上进行基准测试。

话虽如此,由于以下原因,原子应该总是更快:

  • 原子的设计使 JVM 可以利用原子机器指令,这是大多数平台上最快的原子操作
  • synchronized对监控对象使用相对重量级的锁定方案,旨在保护潜在的大代码块。这种形式的锁定本质上比原子操作更复杂,因此您会期望它具有更高的运行时成本。
于 2012-07-19T00:30:53.303 回答
0

正如其他人所说,这取决于实现。但请记住,如果您的程序不变量涉及多个变量,那么您必须使用同步来一起更新它们。您不能仅仅因为它们是原子类型就对两个相关变量一起进行原子操作。在这种情况下,您唯一的朋友是同步的。

于 2012-07-19T07:55:11.340 回答
-3

原子变量总是更快。

你可以看到 java.util.concurrent 包总是使用原子变量而不是同步块。

于 2012-07-18T23:57:11.483 回答