CAS 浅析(原理、实现自旋锁、ABA的产生与解决)

什么是 CAS

CAS(compare and swap),比较与交换。

  • 如果普通的比较与交换
	public void compareAndSwap(int num1, int num2) {
		if (num1 == 10) {
			num2 = 20;
		}
	}

这个代码在 被编译成字节码文件等待一些列操作后,将在 CPU 上执行,而代码翻译成的指令不止一条,那就不能保证原子性。

  • 而在 CPU 中,能用一条指令能够成比较与交换。(伪代码)
// 如果在这个位置(address) 的值等于 这个值(expectedValue),那么交换(newValue)。
boolean CAS(address,expectedValue,newValue) {
	if(address 的 value == expectedValue) {
		address 的 value = newValue;
		return true;
	}
}

至于这个一条指令完成比较与交换和自旋锁有什么关系呢?下面解析!

CAS 原理

CAS 实现自旋锁

自旋锁:线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度。但经 过测算,实际的生活中,大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。基于这个 事实,轻量级锁 / 自旋锁诞生了。也就是说,当某个线程抢占 CPU 失败后,就会继续抢占。

举个栗子
A 今天给女神表白,女神拒绝了,于是 A 明天又来,天天来天天来,直到女神答应他为止。

先看实现的原理(伪代码)

class Lock {
	// 如果在这个位置(address) 的值等于 这个值(expectedValue),那么交换(newValue)。
	boolean CAS(address,expectedValue,newValue) {
		if(address 的 value == expectedValue) {
			address 的 value = newValue;
			return true;
		}
	}
	
	// 记录当前锁是哪个线程持有的, 如果为 null, 那么就表示当前锁没有被持有
	Thread owner = null;
	void Lock() {
		// 循环判断, 如果锁(owner)没有被持有
		// 也就是 owner == null
		// 那么将 Thread.currentThread() 替换上去 
		// 结束循环, 如果 owner != null, 继续循环
		while (!CAS(owner,null,Thread.currentThread()) {
			// 没有内容
		}
	}
}

这就是基本的实现原理

如果不使用 CAS,而是用普通的判断,那么比较交换过程中产生的不止一条指令,如果中途被调度出 CPU,那么就无法完成自旋锁了。
就比如如果使用 两条指令,比较与交换,当比较发现当前锁没有持有线程的时候,正准备交换,突然,被调度出 CPU 了,其他线程占有了这个锁,此时预期的线程就没法占用这个锁。

CAS 实现原子类

由于 CAS 的操作只有一个指令,因此能保证原子性。
普通的什么 count++ 等就不是原子的,因此多个线程想要对一个数进行操作的时候,可能就会导致线程不安全问题。
在 Java 标准款中存在一些原子类,比如描述整数的 AtomicInteger (int),
在这里插入图片描述
在这里插入图片描述
之所以 return …+1,是因为下面返回的是刚开始内存的值,类似于 count++,如果是 ++count,那么就是直接return
在这里插入图片描述

在这里插入图片描述
例如两个线程操作一个数
在这里插入图片描述
就可以保证线程的安全。

ABA 问题

  • 不出现问题
    线程 1 准备给 count - 50
    线程 2 给count + 50,再 count - 50
    在这里插入图片描述
    最终结果是没错的,这就是 ABA 问题的大概样子,用官方话来说就是

ABA 的问题:就是一个值从 A 变成了 B 又变成了 A,而这个期间线程不清楚这个过程。

  • 虽然这个样子看起来没错,但是还是会有意外的。
    在这里插入图片描述

一个通俗点的例子

  • 不出现问题的情况
    A 微信余额有 100块, A 给 B 转账;
    第一次转账 50 卡主了,没成功,此时微信余额还剩 100;
    然后重新转,成功了。此时微信余额还剩 50;
    当转账 A 的任务继续执行的时候,发现余额已经变了,那么就不执行了。
  • 出现问题的情况
    A 微信余额有 100块, A 给 B 转账;
    第一次转账 50 卡主了,没成功,此时微信余额还剩 100;
    然后重新转,成功了。此时微信余额还剩 50;
    此时 C 又给 A 转账 50,此时微信余额为 100;
    当转账 A 的任务继续执行的时候,发现余额没变,那么就继续执行 ,此时微信余额 50;
    实际上微信余额应该是 100 才对(100 - 50 + 50)

如何解决 ABA

给需要修改的内容增加一个属性,该属性记录该内容修改的时间(或者修改的次数也可以)

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值