CAS: Compare And Swap, 即比较和替换。
思想: CAS的比较、替换操作是非阻塞操作, 它有3个参数分别为内存值、预期值和更新值。 当内存值和预期值匹配时则更新, 不匹配时直接返回。
PS: CAS比synchronized效率要好, 因为CAS是c语言实现的cpu锁机制, synchronized是Java锁。
简单的说CAS提供了一种实现原子操作的方法, 都封装在sun.misc.Unsafe类里。 而在实际开发过程中, 并不建议直接操作Unsafe类, 而且使用基于Unsafe类实现的各个派生类。
这些类都提供了原子操作, 假设使用AtomicInteger模拟生产者消费者模式。
AtomicInteger object = new AtomicInteger(0);
for (int i = 0; i < 4; i++) {
//逻辑上的生产者, 值从0到1
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (Exception ex) {
}
System.out.println("判断0线程,当前值是"+ object.get()
+ ", 返回值"+ object.compareAndSet(0, 1)); //compare函数是非阻塞
}
}
}.start();
//逻辑上的消费者, 值从1到0
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (Exception ex) {
}
System.out.println("判断1线程,当前值是" + object.get()
+ ", 返回值" + object.compareAndSet(1, 0)); //非阻塞
}
}
}.start();
}
4个生产者线程、4个开发者线程。 生产者线程的作用是0到1, 消费者线程的作用是1到0。 代码里并没有同步控制逻辑(即没有synchronized/Lock), 运行程序看看效果。
判断0线程,当前值是0, 返回值true
判断0线程,当前值是0, 返回值true
判断1线程,当前值是0, 返回值false
判断1线程,当前值是1, 返回值true
判断0线程,当前值是0, 返回值true
判断1线程,当前值是1, 返回值true
判断1线程,当前值是1, 返回值true
判断0线程,当前值是1, 返回值false
判断0线程,当前值是0, 返回值true
判断0线程,当前值是1, 返回值false
判断1线程,当前值是1, 返回值true
判断1线程,当前值是0, 返回值false
判断0线程,当前值是0, 返回值true
判断1线程,当前值是1, 返回值true
程序运行正常, 并没有报共享冲突错误。
1、 volatile关键字保证这8个线程操作的是同一块内存;
2、 原子性操作compareAndSet即比较并更新, 是在native层实现的, 原理见下面的2篇博客。
CAS里所有ABA问题其实就是值在改回来前不知道发生了什么, 例如: 你读取的值是1, 但前面可能经历了3、2等值的情况。
使用AtomicStampedReference可以解决ABA问题, 因为每次更新值时都有一个时间戳。 在compareAndSet时既要比较值,也要比较时间戳是否一致。
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
参考博客:
https://www.jianshu.com/p/24ffe531e9ee
http://zl198751.iteye.com/blog/1848575