目录
一、什么是CAS
CAS(compare and swap)是一个和多线程密切相关的东西,它会比较寄存器A和内存M中的数据,如果相同,就交换寄存器B和内存M中的数据
CAS是多线程编程中无锁编程的重要组成部分,CAS的存在使得并发编程不需要加锁也可以实现线程安全。
CAS的实际作用,就是多个线程修改同一变量的时候,在修改时确认一下当前变量是不是已经被别的线程修改过,如果修改过了,就读取被修改过的新的变量之后在进行新的修改操作。
还记得线程安全那篇博客里关于两个线程自增5万次的典型案例吗,不使用CAS,就会导致线程不安全,使用CAS,就可以避免这个问题
CAS的特性
CAS是一条CPU指令,也就说,CAS是具有原子性的
基于CAS可以实现很多操作
实现原子类
伪代码实现:
这里的oldValue是一个寄存器,在CAS指令中,如果发现value和oldvalue值相同,就使得oldValue自增然后赋值给value,执行完毕会返回一个true,如果不相同,不会执行并且返回false。
value是一个成员变量,虽然在getAndIncrement()方法中将value的值赋给了oldValue,但是由于cpu的无序调度,很有可能会导致此时CPU调度走,value的值被别的线程修改了,所以CAS再次进行比较,以这种方式保证了线程安全。
标准库中提供了很多原子类
这些原子类,可以保证多线程自增自减时的线程安全
使用方法:
- getAndIncrement():后置++
- incrementAndGet():前置++
- getAndDecrement():后置--
- decrementAndGet():前置--
public static void main(String[] args) throws InterruptedException {
AtomicInteger num = new AtomicInteger(0);
Thread t1 = new Thread(()->{
num.getAndIncrement();//num++;
System.out.println(num.get());
num.incrementAndGet();//++num
System.out.println(num.get());
num.decrementAndGet();//--num;
System.out.println(num.get());
num.getAndDecrement();//num--;
System.out.println(num.get());
});
t1.start();
}
}
使用原子类就可以保证线程安全了,还是以前文中两个线程自增5w次为案例
public class work3 {
public static void main(String[] args) throws InterruptedException {
AtomicInteger num = new AtomicInteger(0);
Thread t1 = new Thread(()->{
for (int i = 0; i < 50000; i++) {
num.getAndIncrement();
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 50000; i++) {
num.getAndIncrement();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(num.get());
}
}
结果为
实现自旋锁
伪代码:
这里的owner是记录当前的锁被哪个线程持有,在lock()中,如果当前线程的锁为空,则将owner设置为当前线程的锁。
CAS的aba问题
CAS关键是对比内存和寄存器中的值是否相同,通过这个对比来检测内存是不是改变过。
但是万一对比的时候相同,但实际上这个值变过,是从a->b->a,看似没变过,实际上变过,就会有一定的概率出现问题。
如何解决
CAS只能对比值是否相同,是无法确定这个值是否改变过的,因此,解决问题的方法就在数据的变化:如果约定数据只能单方面变化,就可以解决这个问题,即数据只能增加,或只能减小。
或者引入版本号:版本号只能增加\减少,但版本号所对应的数据可以随意改变