1、CAS:---> compareAndSet(期望值,修改值)
比较并交换;
/** * CAS: ---> compareAndSet() * 比较并交换 */ public class CASDemo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(5); System.out.println(atomicInteger.compareAndSet(5,2019)+" atomicInteger:"+atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(5,1024)+" atomicInteger:"+atomicInteger.get()); } }
atomicInteger.getAndIncrement();为什么可以保证原子性?
因为底层使用的是Unsafe类:
CAS原理:
unsafe.getAndAddInt()
CAS原语:
CAS总结:
CAS的缺点:
1、循环时间长,开销大;
2、只能保证一个共享变量的原子操作;
3、引发出来的ABA问题?
1、循环时间长,开销大;
2、只能保证一个共享变量的原子操作;
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作;
但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候可以用锁来保证原子性;
3、引发的ABA问题?
JUC下的原子引用封装类:AtomicReference<V>
@Getter @ToString @AllArgsConstructor class User{ String name; int age; } public class AtomicReferenceDemo { public static void main(String[] args) { User z3 = new User("z3",22); User l4 = new User("l4",25); AtomicReference<User> atomicReference = new AtomicReference<>(); atomicReference.set(z3); System.out.println(atomicReference.compareAndSet(z3,l4)+"\t"+atomicReference.get().toString()); System.out.println(atomicReference.compareAndSet(z3,l4)+"\t"+atomicReference.get().toString()); } }
解决ABA问题?AtomicStampedReference<V>();
原子引用+新增一种机制,例如版本号(类似于时间戳);
AtomicStampedReference<V>();
JUC下带有时间戳的原子引用类;
ABA问题产生的示例代码:
public class ABADemo {//ABA问题的解决 AtomicStampedReference<>(); static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); public static void main(String[] args) { new Thread(() -> {//t1线程模拟ABA环境 atomicReference.compareAndSet(100, 101); atomicReference.compareAndSet(101, 100); }, "t1").start(); new Thread(() -> { //t2线程睡眠一秒,保证t1线程执行完ABA问题 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get()); }, "t2").start(); } }
ABA问题的解决示例代码:
public class ABADemo {//ABA问题的解决 AtomicStampedReference<>(); static AtomicReference<Integer> atomicReference = new AtomicReference<>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1); public static void main(String[] args) { System.out.println("====以下是ABA问题的产生===="); new Thread(() -> {//t1线程模拟ABA环境 atomicReference.compareAndSet(100, 101); atomicReference.compareAndSet(101, 100); }, "t1").start(); new Thread(() -> { //t2线程睡眠一秒,保证t1线程执行完ABA问题 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get()); }, "t2").start(); //睡眠2秒,保证t1、t2线程完成 try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("====以下是ABA问题的解决===="); new Thread(() ->{ int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t 第一次的版本号:"+stamp); //t3睡眠一秒,保证t3、t4拿到同一个版本号 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName()+"\t 第二次的版本号:"+atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName()+"\t 第三次的版本号:"+atomicStampedReference.getStamp()); },"t3").start(); new Thread(() ->{ int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t 第一次的版本号:"+stamp); //t4睡眠3秒,保证t3、t4拿到同一个版本号,并且保证t3执行完ABA try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1); System.out.println(Thread.currentThread().getName()+"\t 是否修改成功:"+result +"\t当前实际版本号:"+atomicStampedReference.getStamp() +"\t当前实际最新值:"+atomicStampedReference.getReference()); },"t4").start(); } }