假设有两个计数器实现:
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,操作系统等)?
答案 0 :(得分:5)
incrementAndGet
可以作为CAS循环实现。在高度满足的情况下,可能导致 n -1的线程失败导致 n 线程的O( n )问题。< / p>
(对于@Geek:
通常getAndIncrement
可以实现如下:
int old;
do {
old = value;
} while (!compareAndSet(value, old, old+1));
return old;
想象一下,你有一个 n 线程在同一个原子上执行这个代码,它们碰巧彼此同步。第一次迭代 kn 工作。只有一个CAS会成功。其他 n -1个线程将重复练习,直到只剩下一个。所以总工作量是O( n ^ 2)(最差情况)而不是O( n )。 )
话虽如此,获得一个锁将需要做一些类似的事情,并且锁定在争用时并不是最好的。在使用需要在get和compareAndSwap之前进行大量计算的CAS循环之前,您不太可能看到很多优势。
答案 1 :(得分:3)
此规则被破坏时存在某些情况(例如SMP /单CPU机器,不同的CPU ISA,OS等)?
我不知道。 (我随时准备纠正......如果有人知道具体的反例。)
然而(这是我的主要观点)没有理论的原因,为什么你不能拥有硬件架构或实现不佳的JVM synchronized
速度相同或者比atomic
类型更快。 (这两种同步形式的相对速度是实现问题,因此只能对现有实现进行量化。)
当然,这并不意味着你永远不应该使用synchronized
。 synchronized
构造有许多原子类无法解决的用例。
答案 2 :(得分:2)
它依赖于实现 - 因此最终您需要对您的特定平台/ JVM /配置进行基准测试。
话虽如此,原子应该总是更快,原因如下:
synchronized
使用与监视器对象相对较重的锁定方案,该方案旨在保护潜在的大块代码。这种形式的锁定本质上比原子操作更复杂,因此您可能期望它具有更高的运行时成本。答案 3 :(得分:0)
正如其他人所说,这是依赖于实现的。但请记住,如果您的程序不变量涉及多个变量,那么必须使用同步来一起更新它们。您不能仅仅因为它们是Atomic类型而对两个相关变量进行原子操作。在这种情况下,你唯一的朋友是同步的。
答案 4 :(得分:-3)
原子变量总是会更快。
你可以看到java.util.concurrent包总是使用原子变量而不是同步块。