CAS介绍
- CAS(Compare And Swap) 是由硬件实现的
- CAS可以将read-modify-write这类操作转换为原子操作i++自增操作包括三个自操作:
1.从主内存读取i变量值
2.对i的值加i
3.再把加1之后的值保存到主内存 - CAS原理: 把数据更新到主内存时,在此读取主内存变量的值,如果现在变量的值与期望的值(操作起始时读取的值)一样就更新
一:使用CAS实现线程安全的计数器
1.CASTest01
package com.dome.CASTest;
/**
* @author qb
* @version 1.0
* @Description
* P51 1-2 使用CAS实现线程安全的计数器
* @date 2021/3/9 14:16
*/
public class CASTest01 {
public static void main(String[] args) {
CASCounter casCounter = new CASCounter();
for (int i=0;i<100000;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(casCounter.incrementAndGet());
}
}).start();
}
}
}
class CASCounter{
/**
* 使用volatile修饰value,使线程可见
*/
volatile private long value;
public long getValue(){
return value;
}
/**
* 定义compare and swap
* @return
*/
private boolean compareAndSwap(long expectedValue ,long newValue){
//如果当前value的值与期望的值expectedValue一样,就把当前的value字段替换为newValue的值
synchronized (this){
if(value == expectedValue){
value = newValue;
return true;
}else{
return false;
}
}
}
/**
* 定义自增的方法
*/
public long incrementAndGet(){
long oldValue;
long newValue;
do {
oldValue = value;
newValue = oldValue+1;
}while (!compareAndSwap(oldValue,newValue));
return newValue;
}
}
2.CASTest01 (main方法)
package com.dome.CASTest;
/**
* @author qb
* @version 1.0
* @Description
* P51 1-2 使用CAS实现线程安全的计数器
* @date 2021/3/9 14:16
*/
public class CASTest01 {
public static void main(String[] args) {
CASCounter casCounter = new CASCounter();
for (int i=0;i<100000;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(casCounter.incrementAndGet());
}
}).start();
}
}
}
class CASCounter{
/**
* 使用volatile修饰value,使线程可见
*/
volatile private long value;
public long getValue(){
return value;
}
/**
* 定义compare and swap
* @return
*/
private boolean compareAndSwap(long expectedValue ,long newValue){
//如果当前value的值与期望的值expectedValue一样,就把当前的value字段替换为newValue的值
synchronized (this){
if(value == expectedValue){
value = newValue;
return true;
}else{
return false;
}
}
}
/**
* 定义自增的方法
*/
public long incrementAndGet(){
long oldValue;
long newValue;
do {
oldValue = value;
newValue = oldValue+1;
}while (!compareAndSwap(oldValue,newValue));
return newValue;
}
}
执行结果
二.CAS中的ABA问题
问题出现的原因
- CAS实现原子操作背后有一个假设:共享变量的当前值与当前线程提供的期望值相同,就认为这个变量没有被其他线程修改过。
- 实际上这一种假设不一定总是成立,如果有共享变量 count=0
A线程对count值修改为10
B线程对count值修改为20
C线程对count值修改为0
当前线程看到count变量的值现在是0,现在是否认为count变量的值没有被其他线程更新呢?这种结果是否能接收?
这就是CAS中的ABA问题,即共享变量经历了A->B->A,问题跟实现的算法有关
怎么避免这类问题
如果想避免ABA问题,可以为共享变量引入一个修订号(时间戳),每次修改共享变量时,相应的修订号就会增加1,ABA变量更新过程变为:[A,0] ->[B,1] ->[A,2],每次对共享变量的更改都会导致修订号的增加,通过修订号依然可以判断变量是否被其他线程修改过。(原子变量)AtomicStampedReference类就是基于这种思想产生的