miracle just wanna be better

CAS机制

2019-11-13
miracle

CAS机制

我们知道volatile修饰的变量具有可见性和有序性,但是没有原子性.
比如a++的过程分为3步

  1. 从内存中读取a
  2. 对a进行加1
  3. 将a的值重新写入到内存.

在多线程中,可能有一个线程读完后,还没有写入内存就被其他线程读走了.
解决这个问题可以用AtomicInteger,代码修改了volatile的例子

public class Test {
	private static AtomicInteger  a=new AtomicInteger();
	public static void main(String[] args) {
		Thread[] threads=new Thread[5];
		for(int i=0;i<5;i++){
			threads[i]=new Thread(()->{
				for(int j=0;j<10;j++){
					try {
						System.out.println(a.incrementAndGet());
						Thread.sleep(1000);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			});
			threads[i].start();
		}
	}
}

查看一下incrementAndGet()的源码

public final int incrementAndGet(){
	return unsafe.getAndAddInt(this,valueOffset,1)+1;
}

unsafe调用了getAndAddInt方法

public final int getAndAddInt(Object var1,long var2,int var4){
	int var5;
	do{
		var5=this,getIntVolatile(var1,var2);	
	}while(!this.compareAndSwapInt(var1,var2,var5,var5+var4));
	return var5;
}

底层调用的compareAndSwapInt方法,即cas机制.

分析CAS

过程:包含3个参数,CAS(V,E,N),V表示要更新变量的值,E表示预期,N表示新值.当V等于E,才会将V的值设为N,如果V值和E值不相同,这说明已经有其他线程做了更新,当前线程则什么都不做.

底层原理

public final class Unsafe(){
	public final native boolean compareAndSwapInt(
	Object o,
	long offset,
	int expect,
	int x);
	//还有很多方法
}

用的是C语言实现

解决ABA问题

AtomicStampedReference是一个带有时间戳的对象引用,能够很好的解决ABA问题.

public class AtomicTest {
	private static AtomicInteger index=new AtomicInteger(10);
	static AtomicStampedReference<Integer> stampRef=new AtomicStampedReference<Integer>(10, 1);
	public static void main(String[] args) {
		new Thread(()->{
			int stamp=stampRef.getStamp();
			System.out.println(Thread.currentThread().getName()+"第1次版本号"+stamp);
			stampRef.compareAndSet(10, 11, stampRef.getStamp(), stampRef.getStamp()+1);
			System.out.println(Thread.currentThread().getName()+"第2次版本号"+stampRef.getStamp());
			stampRef.compareAndSet(11, 10,stampRef.getStamp(), stampRef.getStamp()+1 );
			System.out.println(Thread.currentThread().getName()+"第3次版本号"+stampRef.getStamp());
		}).start();
		
		new Thread(()->{
			try {
				TimeUnit.SECONDS.sleep(2);
				int stamp=stampRef.getStamp();
				System.out.println(Thread.currentThread().getName()+"第1次版本号"+stamp);
				boolean isSuccess=stampRef.compareAndSet(10, 12, stampRef.getStamp(), stampRef.getStamp()+1);
				System.out.println(Thread.currentThread().getName()+"修改是否成功"+isSuccess+"当前版本"+stampRef.getStamp());
				System.out.println(Thread.currentThread().getName()+"当前实际值"+stampRef.getReference());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}).start();
	}
}

使用的AtomicStampedReference的compareAndSet函数,里面有4个参数

  1. 预期值
  2. 要更新的值
  3. 预期的时间戳
  4. 更新的时间戳

下一篇 springAOP

Comments

Content