-
CAS的意思
CAS 意思就是compareAndSet,比较并设值。
如果变量的值与期望值相等,则设置新值。
-
示例
public class CASDemo1 { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(100); //CAS 意思就是compareAndSet //如果当前值与期望值相等 则修改 System.out.println(atomicInteger.compareAndSet(100, 101)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(100, 101)); System.out.println(atomicInteger.get()); } }
结果
-
ABA问题
现在有t1,t2两条线程。同时操作var这个变量。t1线程期望的是var==1,则修改var=2。但是同时t2也在操作var,t2线程先执行,它先把var变成了3,然后再把var变成了1。这时候t1执行的时候看到的var虽然是1,但是是已经被修改过的。这就是ABA问题。
解决这个问题的一个办法就是给var加一个版本号,不管是谁修改了var都对这个版本号加1。其他线程在修改时直接判断版本号。相当于乐观锁。
-
原子引用
带版本号的原子操作,就是一个乐观锁,可以解决ABA问题。
public class CASDemo2 { public static void main(String[] args) { //带版本号的原子应用 AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1); new Thread(()->{ int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"->" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //期望值和期望的版本号都匹配上 则修改值 boolean res = atomicStampedReference.compareAndSet(1,2, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"->" + res); boolean res2 = atomicStampedReference.compareAndSet(2,1, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"->" + res); System.out.println(Thread.currentThread().getName()+"->" + atomicStampedReference.getStamp()); },"a").start(); new Thread(()->{ int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"->" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } boolean res = atomicStampedReference.compareAndSet(1,6, stamp,stamp+1); System.out.println(Thread.currentThread().getName()+"->" + res); System.out.println(Thread.currentThread().getName()+"->" + atomicStampedReference.getStamp()); },"b").start(); }
上面的代码中,如果线程a先执行修改操作,就算最后的值被改回了1,但是版本号已经变了,线程b再执行不会修改成功。解决了ABA的问题。
注意,这里的泛型类Integer如果设置超过了127会出现问题
-
cas的内部是一个自旋锁
一直比较线程存储区的值和主存中的值,如果相等则修改,否则一直循环
这样会产生一个问题,如果现在有100个线程在调用,那么最后一个线程执行成功时已经自旋了100次,浪费资源。
LongAdder类可以解决这个问题。