Java高性能编程之CAS与ABA及解决方法
前言
如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法。
CAS概念
CAS,全称Compare And Swap,比较与交换。
属于硬件级别的同步原语,从处理器层面提供了内存操作的原子性。
从概念上,我们可以得出三点。第一,CAS的运作方式(通过比较与交换实现)。第二,硬件层面支持,性能肯定不低(当然它也不是银弹)。第三,提供原子性,那么它的功能肯定是确保原子性,从而确保线程安全。
实际使用中,CAS操作需要输入两个数值,一个旧值A(期望操作前的值)和一个新值B,在操作期间先将旧值A与实际内存中的值进行比较,如果没有发生变化,才将实际内存中的值交换成新值B,如果发生了变化则不交换。
CAS应用场景
既然CAS的功能是提供原子性,那么从这个角度出发思考,如计数器,账户转账等。
那么提到计数器,就不得不提到JUC包下的atomic包了。其中提供了大量原子操作了,如Integer类型的值变化,Long类型的值变化,Boolean类型的值变化。
说到这里,某些人就要一句“球多麻袋”,Integer类型的值变化,不就一句代码嘛(如i = i + 1;),不就是原子操作嘛。即使有赋值操作,也可以写成(i++;),这样不就一个操作了嘛。当然学习过汇编或对计算机指令有一定了解的朋友可能就知道原因了。很多时候,我们在程序中的一段代码,编译到底层执行时,往往是多个语句(谁让CPU只能执行非常简单的操作呢)。如i++操作编译成Java指令后是以下四句:
- getfield
- iconst_1
- iadd
- putfield
具体的意思,我就不解读了(不是今天的重点),感兴趣的,可以百度或者@我。
话题回到JUC包下的atomic包,我之所以提它,就是因为其原子性的实现就是依靠CAS实现的。
AtomicInteger类:
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
Unsafe类:
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
}
public final native boolean compareAndSwapInt(Object var1,
long var2, int var4, int var5);
通过上述三段源码,可以清楚看出,AtomicInteger中getAndSet这一原子方法是通过Unsafe中的原生方法compareAndSwapInt方法完成CAS机制,从而确保操作的原子性。
CAS还涉及到Java中锁的实现,这个也留到锁专题再细说,毕竟这次的主题是CAS,ABA及解决之道。
Why need CAS
那么为什么需要CAS呢,毕竟Java已经有了多种手段来保证线程安全的原子性问题,最广为人知的除了Atomic包(底层是CAS),就是synchronized锁了。
原因很简单,因为synchronized锁什么的太重了。这里所说的重,是指其消耗的系统资源较多(所以又称为重量级锁)。所以有着底层硬件支持的CAS才会那么受欢迎。当然CAS也有着自己的问题,这个后面会谈到。
CAS应用:
说得再多,不如来点实际代码,看看具体效果。
以下代码,包含四个类:一个主类,用于调用实现类,展示效果(注释中有执行结果)。三个实现类,分别展示了没有处理,使用Atomic包,使用CAS三种方式来多线陈增加全局计数器的效果。
AtomicityWithNoDeal
未做任何处理,通过100个子线程分别执行10000次计数器+1操作。
package tech.jarry.learning.netease;
/**
* @Description:
* @Author: jarry
*/
public class AtomicityWithNoDeal {
private volatile int i = 0;
private void add(){
i++;
}
public void run() throws InterruptedException {
for (int j = 0; j < 100; j++){
new Thread(new Runnable() {
@Override
public void run() {
for (int m = 0; m< 10000; m++){
add();
}
System.out.println(Thread.currentThread().getName()+" has run finished !");
}
}).start();
}
Thread.sleep(2000);
System.out.println("i: "+i);
}
}
AtomicityWithAtomic
进行Atomic包处理,通过100个子线程分别执行10000次计数器+1操作。
package tech.jarry.learning.netease;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description:
* @Author: jarry
*/
public class AtomicityWithAtomic {
private AtomicInteger atomicInteger = new AtomicInteger(0);
private void add(){
atomicInteger.incrementAndGet();
}
public void run() throws InterruptedException {
for (int j = 0; j < 100; j++){
new Thread(new Runnable() {
@Override
public void run() {
for (int m = 0; m< 10000; m++){
add();
}
System.out.println(Thread.currentThread().getName()+" has run finished !");
}
}).start();
}
Thread.sleep(2000);
System.out.println("atomicInteger: "+atomicInteger.get());
}
}
AtomicityWithCAS
进行手写的CAS处理,通过100