JUC高并发-CAS

定义与原理

COMPARE AND SWAP ,比较并替换;

 

比较如果为预期值,则按设定值进行替换

 

 

实例演示

实例1、SWAP实例演示

private static void test01() {

//初始化默认值为0

AtomicInteger inte=new AtomicInteger();

//变1

inte.getAndIncrement();

//如果为1,则变成30

System.out.println(inte.compareAndSet(1, 30));

//如果为30,则变成5

System.out.println(inte.compareAndSet(30, 5));

}

结果:

true

true

 

实例2、getAndIncrement() 方法源码解析:

// unsafe可以直接操作内存

public final int getAndIncrement() {

// this 调用的对象

// valueOffset 当前这个对象的值的内存地址偏移值

// 1

return unsafe.getAndAddInt(this, valueOffset, 1);

}

public final int getAndAddInt(Object var1, long var2, int var4) {

int var5; // ?

do { // 自旋锁(就是一直判断!)

// var5 = 获得当前对象的内存地址中的值!

var5 = this.getIntVolatile(this, valueOffset); // 1000万

// compareAndSwapInt 比较并交换

// 比较当前的值 var1 对象的var2地址中的值是不是 var5,如果是则更新为 var5 + 1

// 如果是期望的值,就交换,否则就不交换!

} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;

}

缺点:

1、循环开销很大!

2、内存操作,每次只能保证一个共享变量的原子性!

3、出现ABA 问题?

实例3、ABA问题:

多个线程针对一个资源同时修改,此资源值被线程修改多次后变回原来的初始值,此刻cas无法识别其值是否已发送过变动,直接进行识别替换。

例如 值 t=5 ,线程A修改后为4,线程B修改后为5,此时线程C判断如果t=5,则值变为6;

static AtomicInteger common=new AtomicInteger(5);

//ABA问题演示

private static void test02() throws InterruptedException {

CountDownLatch count=new CountDownLatch(2);

new Thread(()->{

common.getAndIncrement();

System.out.println(common.get());

count.countDown();

}).start();

new Thread(()->{

common.getAndDecrement();

System.out.println(common.get());

count.countDown();

}).start();

 

count.await();

System.out.println("如果为5则替换为3:"+common.compareAndSet(5, 3));

}

结果:

6

5

如果为5则替换为3:true

 

实例4、ABA问题解决, 采用AtomicStampedReference方式,比较版本号和值

static AtomicStampedReference<Integer> atomicReference = new AtomicStampedReference<>(100,1);

private static void test03() {

// 其他人员 小花,需要每次执行完毕 + 1

new Thread(()->{

int stamp = atomicReference.getStamp();// 获得版本号

System.out.println("T1 stamp01=>"+stamp);

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

 

atomicReference.compareAndSet(100,101,

atomicReference.getStamp(),atomicReference.getStamp()+1);

System.out.println("T1 stamp02=>"+atomicReference.getStamp());

 

atomicReference.compareAndSet(101,100,

atomicReference.getStamp(),atomicReference.getStamp()+1);

System.out.println("T1 stamp03=>"+atomicReference.getStamp());

},"T1").start();

 

 

// 乐观的小明

new Thread(()->{

int stamp = atomicReference.getStamp();// 获得版本号

System.out.println("T2 stamp01=>"+stamp);

try {

TimeUnit.SECONDS.sleep(3);

} catch (InterruptedException e) {

e.printStackTrace();

}

boolean result = atomicReference.compareAndSet(100, 1, stamp, stamp + 1);

System.out.println("T2 是否修改成功:"+ result);

System.out.println("T2 stamp02=>"+atomicReference.getStamp());

System.out.println("T2 当前获取得最新的值=>"+atomicReference.getReference());

},"T2").start();

}

 

结果:

T2 stamp01=>1

T1 stamp01=>1

T1 stamp02=>2

T1 stamp03=>3

T2 是否修改成功:false

T2 stamp02=>3

T2 当前获取得最新的值=>100

 

 

 

应用场景

CAS是一种应用理念,即乐观锁机制;采用CAS方式也可进行数据库更新

举例说明:

库存表,初始化值 5,版本号 1;其下分别有三个线程 A、B、C,

线程A :将库存更新为 4,版本号更新为 2

线程B :将库存更新为 5,版本号更新为 3

线程C: 如果值为5,且版本号为1,则更新

 

执行顺序为: A->B->C   .

结果推导: 版本号为3,C不会更新

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值