CAS:全称Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:
我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。
-
比较 A 与 V 是否相等。(比较)
-
如果比较相等,将 B 写入 V。(交换)
-
返回操作是否成功。
标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.
典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作.
AtomicInteger atomicInteger = new AtomicInteger(0); // 相当于 i++ atomicInteger.getAndIncrement()
class AtomicInteger {
private int value;
public int getAndIncrement() {
int oldValue = value;
while ( CAS(value, oldValue, oldValue+1) != true) {
oldValue = value;
}
return oldValue;
}
}
假设两个线程同时调用getAndIncrement,两个线程都读取value到自己的oldvalue中。线程1先执行CAS操作,由于value和oldvalue相同,直接对value进行赋值。
这个时候线程2再次执行CAS操作,发现oldvalue和value不相同,不能进行赋值操作,需要循环,在循环中重新读取value的值赋值给oldvalue,然后在进行赋值操作。
实现自旋锁自旋锁指的是获取锁失败的线程不进入阻塞状态,而是在cpu上进行空转(线程不让出cpu,而是跑一些无用的指令),不断查询当前锁的状态
CAS的ABA问题假设存在两个线程 t1 和 t2. 有一个共享变量 num, 初始值为 A. 接下来, 线程 t1 想使用 CAS 把 num 值改成 Z, 那么就需要先读取 num 的值, 记录到 oldNum 变量中.使用 CAS 判定当前 num 的值是否为 A, 如果为 A, 就修改成 Z. 但是, 在 t1 执行这两个操作之间, t2 线程可能把 num 的值从 A 改成了 B, 又从 B 改成了 A
到这一步线程A无法区分当前这个变量到底是A,还是经历了一个变化过程
解决方案给要修改的值引入版本号,CAS每次修改时也要确定版本号是否符合预期
Synchronized原理基本特点
-
开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
-
开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
-
实现轻量级锁的时候大概率用到的自旋锁策略
-
是一种不公平锁
-
是一种可重入锁
-
不是读写锁
什么是死锁
参考哲学家进餐问题
有个桌子, 围着一圈哲学家, 桌子中间放着一盘意大利面. 每个哲学家两两之间, 放着一根筷子.
比特就业课每个哲学家 只做两件事: 思考人生 或者 吃面条. 思考人生的时候就会放下筷子. 吃面条就会拿起左
右两边的筷子(先拿起左边, 再拿起右边).
如果哲学家发现筷子拿不起来了(被别人占用了), 就会阻塞等待.
[关键点在这] 假设同一时刻, 五个哲学家同时拿起左手边的筷子, 然后再尝试拿右手的筷子, 就会
发现右手的筷子都被占用了. 由于哲学家们互不相让, 这个时候就形成了 死锁
家发现筷子拿不起来了(被别人占用了), 就会阻塞等待.
[关键点在这] 假设同一时刻, 五个哲学家同时拿起左手边的筷子, 然后再尝试拿右手的筷子, 就会
发现右手的筷子都被占用了. 由于哲学家们互不相让, 这个时候就形成了 死锁



