0%

CAS

compare and set(swap) —— 无锁/自旋锁

通俗来讲就是 比较和替换或者改变。CAS 是不用上锁的,所以也称之为无锁或自旋锁。
例子:t1 线程读取了一个变量 a 的值为 1,现在将 a 的值改为 2,做写入操作之前,拿之前取到的 1 这个值和现在 a 的值进行比较,如果相等,也就是说没有其他线程对这个 a 有过修改,这时候就可以放心地把 2 写入了;如果比较不相等,则说明了有其他线程对 a 这个值有修改,这时候不能写入并且再继续之前的操作。之前的操作即为如果我拿 1 去和 a 的当前值比较的时候,因为 a 被其他线程修改为了 5,这个时候 t1 会重新读取 a 的变量,但是这个时候 a 的值并不为 1 了,而是为 5 了,然后我们又对它进行了修改成为 2,再写入之前,又用读取到的 5 去和当前 a 的值比较,一样,则证明这期间没有被修改,可做写入操作,反之不可做写入操作且继续上一步。

CAS 会带来的问题

  • 循环时间长,开销大
  • 只能保证一个共享变量的操作
  • ABA 问题

ABA 问题

概述:就是说 之前 a 变量的值为 0,t1 第一个去拿出来改为了 1,未做写入操作之前发生了这样的场景:t2 线程也拿出来(此时拿出来的是 0)并且改为 3 写入了,然后 t3 线程又拿出来(此时拿出来的是 t2 写入的,也就是 3)修改为 0 并且写入了。这个时候 t1 做 CAS 的时候,并没有发现 a 变量已经被改变为其他又被改回来了,所以能正常做写入操作。
解决方案:加一个版本号(貌似有点乐观锁的感觉。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// ABA问题
public class T {
static AtomicInteger atomicInteger = new AtomicInteger();
public static void main(String[] args) {
System.out.println("---ABA问题---");
System.out.println("原始值:" + atomicInteger.get());
//0-->1-->0
new Thread(() -> {
atomicInteger.compareAndSet(0, 1);
System.out.println("步骤一:" + atomicInteger.get());
try {
//线程A休眠1秒,保证线程A按照0-->1-->0执行
TimeUnit.SECONDS.sleep(1);
atomicInteger.compareAndSet(1, 0);
System.out.println("步骤二:" + atomicInteger.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread A").start();

new Thread(() -> {
//线程B休眠3秒,保证线程A完成ABA问题
try {
TimeUnit.SECONDS.sleep(3);
atomicInteger.compareAndSet(0, 2);
System.out.println("步骤三:" + atomicInteger.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "thread B").start();
while (Thread.activeCount() > 2) {}
System.out.println("最终结果:" + atomicInteger.get());
}
}

#

案例
https://baijiahao.baidu.com/s?id=1730331465639596673픴=spider&for=pc