在我们深入了解CAS(Compare And Swap)策略以及它是如何在AtomicInteger
这样的原子构造器中使用的,首先来看一下这段代码:
public class MyApp
{
private volatile int count = 0;
public void upateVisitors()
{
++count; //increment the visitors count
}
}
这里的代码记录的访问应用的访客的数量。这段代码有问题么?如果多个线程试图更新这个数值会发生什么?事实上,这里的问题在于单单将count
标记为volatile
并不能保证原子性,++count
也不是一个原子操作。想要了解更多请查看这里。
那么如果我们将方法标记为synchronized
可以解决这个问题吗?
public class MyApp
{
private int count = 0;
public synchronized void upateVisitors()
{
++count; //increment the visitors count
}
}
这段代码可以保证原子性吗?可以。 这段代码可以保证可见性啊?可以。
那这里还有什么问题?
它使用了锁从而引入了大量的延时和。详情查看这里。这种方式开销太大。
为了解决这个问题引入了原子构造器。如果我们使用AtomicInteger
来记录访问量,也可以达到目的。
public class MyApp
{
private AtomicInteger count = new AtomicInteger(0);
public void upateVisitors()
{
count.incrementAndGet(); //increment the visitors count
}
}
支持原子操作的类如AtomicInteger
,使用CAS来实现。CAS并没有使用锁,而是以一种很乐观的方式来处理。它遵循以下几步:
看一下AtomicLong
类中的代码:
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
在JDK 8中上面的代码被更改为一行代码:
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
这一行代码有何优点?
实际上,这一行是会由JIT翻译为优化的指令序列的JVM内部函数。在x86架构中它就是一条CPU指令LOCK XADD,会比CAS循环的性能好很多。
现在考虑一下当我们有较高的争用以及一些线程想要更新相同的原子变量的可能性。在这种情况下,锁可能会优于原子变量,但在实际的争用级别中,原子变量的性能优于锁。在Java 8 中引入了另外一个构件LongAdder
。
LongAdder
并不完全是AtomicLong
的替代品,我们需要考虑以下因素:
AtomicLong
性能更好