Java并发编程- CAS原理
例子:
/**
* CAS原理
* @author wufei
* @create 2019-07-23 19:03
**/
public class CAS1 {
private static volatile int m =0;
//原子性的保证
private static AtomicInteger atomicInteger = new AtomicInteger(0);
private static void incress1(){
m ++;
}
private static void incress2(){
atomicInteger.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException{
Thread[] th1 = new Thread[20];
for (int i = 0; i < 20; i++) {
th1[i] = new Thread(new Runnable() {
public void run() {
CAS1.incress1();
}
});
th1[i].start();
th1[i].join();
}
Thread[] th2 = new Thread[20];
for (int i = 0; i < 20; i++) {
th2[i] =new Thread(new Runnable() {
public void run() {
CAS1.incress2();
}
});
th2[i].start();
th2[i].join();
}
System.out.println("m: "+m);
System.out.println("atomicInteger: "+atomicInteger.get());
}
}
运行都是输出20 20
反编译:
javap -v CAS1.class
javap -c CAS1.class
我们查看的只需要incress1()和incress2()
从编译的class文件可以看出:ingress1方法的m++执行了三个指令,
ingress2方法的atomicInteger.incrementAndGet();执行了一个指令,说明了atomicInteger.incrementAndGet()效率高于m++;
我们来看下atomicInteger.incrementAndGet()源码来分析底层原理:
使用的是sun.misc包下的Unsafe类。
调用的方法getAndAddInt()
参数:var1: 要修改的值
var2: 期望值
var4: 更新的值
意思是要修改的值和期望值对比,如果一样则更新值,否则不更新。
原理过程: 原子性AtomicInteger.
incrementAndGet()方法,通过Unsafe类vm去调用unsafe.cpp--汇编。(需要硬件支持)
例子2:
线程1 把100 修改了110
线程2 把110修改了100
线程3 把100修改了120
出现了ABA的问题
public class CAS2 { static AtomicInteger atomicInteger = new AtomicInteger(100); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { public void run() { System.out.println(atomicInteger.compareAndSet(100, 110)); } }); t1.start(); Thread t2 = new Thread(new Runnable() { public void run() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicInteger.compareAndSet(110, 100)); } }); t2.start(); Thread t3 = new Thread(new Runnable() { public void run() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicInteger.compareAndSet(100, 120)); } }); t3.start(); } }
输出结果:都修改成功了,其实应该t1和t2成功,t3是更新失败的。
怎样解决ABA的问题呢?
使用AtomicStampedReference类,使用了版本号来控制
public class CAS3 { //初始化asr,然后设置了版本号为0 static AtomicStampedReference asr = new AtomicStampedReference(100,0); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { public void run() { System.out.println(asr.compareAndSet(100, 110, asr.getStamp(), asr.getStamp() + 1)); System.out.println(asr.compareAndSet(110, 100, asr.getStamp(), asr.getStamp() + 1)); } }); t1.start(); Thread t2 = new Thread(new Runnable() { public void run() { int st = asr.getStamp();//获取版本号 try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(asr.compareAndSet(110, 100, st, st+ 1)); } }); t2.start(); } }
运行结果:
CAS的使用场景:
1.应用于简单的数据计算
2.适合线程冲突少的场景