Java 同步块能比原子更快吗?

Java 同步块能比原子更快吗?,java,concurrency,java.util.concurrent,Java,Concurrency,Java.util.concurrent,假设以下两种计数器实现: class Counter { private final AtomicInteger atomic = new AtomicInteger(0); private int i = 0; public void incrementAtomic() { atomic.incrementAndGet(); } public synchronized void increment() { i++; } } 乍一看,原子学应该更快、

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

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

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

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

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

原子变量总是更快

您可以看到java.util.concurrent包始终使用原子变量,而不是同步块

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

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

但是(这是我的主要观点)没有理论上的理由说明为什么不能有一个硬件体系结构或一个实现糟糕的JVM,其中
synchronized
的速度与
原子类型的速度相同或更快。(这两种同步形式的相对速度是一个实现问题,因此只能对现有实现进行量化。)


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

它依赖于实现,因此最终需要在特定的平台/JVM/配置上进行基准测试

话虽如此,原子学应始终更快,原因如下:

  • 原子的设计使JVM可以利用原子机器指令,这是大多数平台上最快的原子操作
  • synchronized
    对监视器对象使用相对较重的锁定方案,旨在保护可能较大的代码块。这种形式的锁定本质上比原子操作更复杂,因此您可能期望它具有更高的运行时成本

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

(对于@Geek:

通常,
getAndIncrement
可以实现如下功能:

 int old;
 do {
     old = value;
 } while (!compareAndSet(value, old, old+1));
 return old;
假设您有n个线程在同一个原子上执行此代码,并且它们恰好彼此同步。第一次迭代不起作用。只有一个CA会成功。其他n-1个线程将重复此练习,直到只剩下一个。因此,总的工作量是O(n^2)(最坏情况)而不是O(n)。)


话虽如此,获得一个锁最多也需要做一些类似的事情,而当竞争激烈时,锁并不是处于最佳状态。在使用CAS循环之前,您不太可能看到锁的许多优点,因为CAS循环在获取和比较DSAP之前需要大量计算

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

第二种说法虽然正确,但不能证明任何事情。事实上,这只意味着原子类型的平均速度更快。。。对于那些特定的用例。。。在Oracle支持的ISA/平台上。为什么?如果你的第二句话应该提供理由,那就没有了。并发包声明的目的是不在任何地方使用同步。在极少数情况下,乐观锁定的原子变量速度较慢,我提到的原因是为了表明它们是首选解决方案,并且在大多数情况下可能是更好的解决方案;并发包在内部所做的并不是性能的证据;它只表明并发包满足其设计目标。如果它不使用自己的类,那将是令人惊讶的。是的,原子能保证内存的可见性,但不能保证相互执行。你能详细说明你的第一段吗?