恰谈我对CAS的理解

volatile引起的思考

首先CAS是一种思想和算法,Compare And Swap,即比较并交换,是对volatile的补充和延申,我们知道volatile只能保证可见性和禁止指令重排序,但是不能解决原子操作,最典型的就是a++操作。
在并发情况下,a++会产生线程安全问题,主要是因为java内存模型和线程竞争机制引起,这里回顾一下a++在高并发下的安全问题。所为原子性是指一系列操作不可分割和打断,要么都成功要么都不成功,而a++执行分三个步骤,首先主内存中读取数值到工作内存操作,然后修改数值,第三步是写回主内存,如果是单线程这没什么安全问题,但是多线程同时去修改就会产生问题,比如线程A读取a的值是1还没来得及修改,线程B再读取a的值并修改为2,这时线程A继续工作,这个时候线程A还认为a的值是1,并加1,再将a等于2写入内存中,实际情况是a的值应该是3了,下面是代码演示:

public class S {
	volatile static int a;

	public static void main(String[] args) throws InterruptedException {
		Thread thread = new Thread(new VoDemo());
		Thread thread1 = new Thread(new VoDemo());
		thread.start();
		thread1.start();
		thread.join();
		thread1.join();
		System.out.println(a);
	}

	static class VoDemo implements Runnable {

		@Override
		public void run() {
			for (int i = 0; i < 10000; i++) {
				a++;
			}
		}
	}
}

结果是14767,执行了20000次,明显线程不安全。
这是由于volatile不能保证原子性的原因,而利用CAS且能保证原子性,所谓CAS原理就是认为v的值是A,如果是把v的值变成B,如果不是A,说明有人修改过了,那就不再修改,避免同时多人修改照成错误。

共有三个操作数:内存值V,预期值A、要修改的值B,当且仅仅预期值A等于内存值V、才将内存值改为B,否则什么都不做,最后返回V,最终实现原子性,他是属于乐观锁,认为每次没人会来抢,所以不用加锁,在并法包和原子类用的比较多

下面对代码进行改造

public class S {
//原子整形,利用CAS原理保证原子性
static AtomicInteger a=new AtomicInteger();

	public static void main(String[] args) throws InterruptedException {
		Thread thread = new Thread(new VoDemo());
		Thread thread1 = new Thread(new VoDemo());
		thread.start();
		thread1.start();
		thread.join();
		thread1.join();
		System.out.println(a);
	}

	static class VoDemo implements Runnable {

		@Override
		public void run() {
			for (int i = 0; i < 10000; i++) {
				a.incrementAndGet();
			}

		}

	}
}

发现两个线程同时执行共20000次结果为20000

代码演示CAS原理

public class S {
	static int expectedValue;

	public static void main(String[] args) throws InterruptedException {
		compareAndSet(0, 1);
	}

	private static int compareAndSet(int a, int b) {
	//判断预期值
		if (expectedValue == a ) {
		//如果等于预期值就更新
			expectedValue = b;
		}else {
		//否则返回原来的值
			return expectedValue;
		}
		return expectedValue;
	}
}

CAS的问题

同样CAS也会存在风险,那就是ABA问题,所谓ABA就是一个线程将值改为B,另外一个线程又改为A,第三个线程修改的时候发现值还是A,实际上已经有修改过,解决办法就是做一个版本号,每次修改都会在版本号上加1

CAS存在自旋问题,在并发下不断地比较自旋会消耗CPU资源

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值