CAS
1.CAS
cas通俗点说就是比较和交换,synchronized是一种悲观锁,那么cas就是乐观锁;
悲观锁:就是线程独占,只有当这个线程结束,才能有其它线程使用
乐观锁:乐观锁就是不加锁,假设没有冲突,而完成某项操作,如果冲突失败就重试
实例:
1 public class CASdemo { 2 public static void main(String[] args) { 3 AtomicInteger atomicInteger=new AtomicInteger(0); 4 for (int i = 0; i <20 ; i++) { 5 new Thread(()->{ 6 for (int j = 0; j <100 ; j++) { 7 atomicInteger.getAndIncrement(); 8 } 9 },String.valueOf(i)).start(); 10 } 11 12 try{ 13 TimeUnit.SECONDS.sleep(3);//估计3秒上面的20个线程跑完 14 }catch (InterruptedException e){ 15 e.printStackTrace(); 16 } 17 System.out.println(atomicInteger.get()); 18 } 19 }
原子类AtomicInteger源码:
1 //成员变量Volatile保证了可见性,以及禁止指令重排 2 private volatile int value; 3 4 //++方法 5 public final int getAndIncrement() { 6 return unsafe.getAndAddInt(this, valueOffset, 1); 7 } 8 9
unSafe类
1 //var1为对象 var2就是value内存偏移量就是的内存地址, 2 public final int getAndAddInt(Object var1, long var2, int var4) { 3 int var5; 4 do { 5 //到var1这个对象,var2位置拿值 6 var5 = this.getIntVolatile(var1, var2); 7 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//这个方法,是先判断现在拿到的值,是不是还是那个值,如果是,就替换,返回true,自旋结束,否则false,重新拿值;这就是一种乐观锁 8 return var5; 9 } 10 11 //下面这两个方法都是本地方法,它们的底层都是C++实现的,是系统的原语,具有原子性 12 public native int getIntVolatile(Object var1, long var2); 13 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
4.CAS缺点
1.循环时间长开销时间大
有一个自旋,所以时间开销大
2.只能保证一个共享变量的原子操作
只能保证一个对象
3.ABA问题
例子:
1 AtomicInteger a=new AtomicInteger(0); 2 a=0; 3 4 //假如线程1运行时间是20,将a变为10 5 //线程2运行时间是5,将a变为5或0; 6 7 //当线程运行时 8 //线程1运行 此时拿到a=0 9 //线程2运行 将a变为5 时间过5 10 //线程2再次运行 将a变为0 时间过5 11 //时间过10 ,到线程1要写值时a=0余主内存a=0一致将 a=10写入 12 13 //此时a已经被改写过,只不过还是原来的值,但是线程1不知道,这就是aba的问题
如何规避这个ABA问题?
简单直接,加个时间戳
代码实例:
1 class User{ 2 String name; 3 byte age; 4 } 5 public class ABAdemo { 6 public static void main(String[] args) { 7 User A=new User(); 8 A.name="A"; 9 A.age=18; 10 User B=new User(); 11 B.name="B"; 12 B.age=20; 13 //一个对象,1为版本号 14 AtomicStampedReference<User> atomicStampedReference = new AtomicStampedReference<>(A,1); 15 System.out.println("获得对象"+atomicStampedReference.getReference()); 16 17 //对象比对,版本号比对 18 atomicStampedReference.compareAndSet(A,B,1,2); 19 System.out.println("修改后"+atomicStampedReference.getReference()); 20 } 21 }