洛阳市涧西区建设局网站,怎样成立网站,苏州建设交通高等职业技术学校,小程序制作卡片列表一、案例CAS是Java中Unsafe类里面的一个方法#xff0c;它的全称是叫CompareAndSwap比较并交换的一个意思#xff0c;它的主要功能是能够去保证在多线程的环境下对于共享变量修改的一个原子性。例如#xff0c;比如说像这样一个场景#xff0c;有一个成员变量state#xf…一、案例CAS是Java中Unsafe类里面的一个方法它的全称是叫CompareAndSwap比较并交换的一个意思它的主要功能是能够去保证在多线程的环境下对于共享变量修改的一个原子性。例如比如说像这样一个场景有一个成员变量state默认值是0其中定义了一个方法叫doSomething()这个方法的逻辑是先判断state是否为0如果为0就修改成1。这个逻辑在单线程的情况下看起来没有任何问题但是在多线程的环境下会存在原子性的问题因为这里是一个典型的Read和Write的一个操作一般情况下我们会在doSomething()这个方法去加一个synchronized的同步锁来解决这样的一个原子性的问题但是加同步锁一定会带来性能上的损耗所以对于这一类的场景我们可以使用CAS机制来进行优化。优化后的代码在doSomething()这个方法中调用了unSafe类中的compareAndSwapInt()方法来达到同样的目的。这个方法有四个参数分别是当前对象实例成员变量state在内存地址中的一个偏移量预期值0和期望更改之后的值1。CAS机制会比较state内存地址偏移量对应的值和传入的预期值0是否相等如果相等就直接修改内存地址中state的值等于1否则返回false表示修改失败而这个过程它是一个原子的不会存在任何线程安全的问题。比较线程的期望值与物理内存的真实值CompareAndSwap是一个native方法实际上它最终还是会面临同样的问题就是先从内存地址中读取state值然后再去比较最后再去修改这过程不管是在什么层面去实现都会存在原子性问题所以在CompareAndSwap的底层实现里面如果是在多核的CPU环境下会增加一个lock指令来对缓存或者总线去加锁从而去保证比较并替换这两个操作的原子性。CAS主要是应用在一些并发场景里面比较典型的使用场景有两个第一个是J.U.C里面的Atomic包里面的原子性实现比如AtomicInteger、tomicLong第二个是实现多线程对共享资源竞争的互斥性质比如AQS、ConcurrentHashMap、ConcurrentLinkedQueue等。二、Unsafe类1、Unsafe类Unsafe是CAS的核心类由于Java方法无法直接访问底层系统需要通过本地native方法来操作Unsafe相当于一个后门基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中其内部方法操作可以像C的指针一样直接操作内存因为Java中CAS操作的执行依赖于Unsafe类的方法。注意UnSafe类中的所有方法都是native修饰的也就是说UnSafe类中的方法都是直接调用操作系统底层资源执行相应任务。2、变量valueOffset表示该变量值在内存中的偏移地址因为Unsafe就是根据内存偏移地址获取数据的。3、变量value用volatile修饰保证了多线程之间的内存可见性三、CASCAS全称是叫CompareAndSwap比较并交换它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值如果是则更改为新的值这个过程是原子的。CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能通过它实现了原子操作。再次强调由于CAS是一种系统原语原语属于操作系统用语范畴是由若干指令组成的用于完成某个功能的一个过程并且原语的执行必须是连续的在执行过程中不允许被中断也就是说CAS是一条CPU的原子指令不会造成所谓的数据不一致问题。四、CAS缺点循环时间长、开销大getAndAddInt方法执行时有个do while如果CAS失败会一直进行尝试。如果CAS长时间一直不成功可能会给CPU带来很大的开销。只能保证一个共享变量的原子操作当对一个共享变量执行操作时我们可以使用循环CAS的方式来保证原子操作但是当对多个共享变量操作时循环CAS就无法保证操作的原子性这个时候就可以用锁来保证原子性。引出来ABA问题五、ABA问题1、ABA问题CAS会导致“ABA问题”。CAS算法实现一个重要前提需要取出内存中某时刻的数据并且在当下时刻比较并替换在这个时间差类会导致数据的变化。比如所一个线程one从内存地址V中取出A这时候另一个线程two也从内存中取出A并且线程two进行了一些操作将值变成了B然后线程two又将V位置的数据变成了A这时候线程one进行CAS操作发现内存中仍然是A然后线程one操作成功。尽管线程one的CAS操作成功但是不代表这个线程就是没有问题的。2、AtomicReference原子引用Getter
ToString
AllArgsConstructor
class User {String userName;int age;
}public class AtomicReferenceDemo {public static void main(String[] args) {User z3 new User(z3, 22);User li4 new User(li4, 25);AtomicReferenceUser atomicReference new AtomicReference();atomicReference.set(z3);System.out.println(atomicReference.compareAndSet(z3, li4) \t atomicReference.get().toString());System.out.println(atomicReference.compareAndSet(z3, li4) \t atomicReference.get().toString());}
}3、ABA问题解决AtomicStampedReference版本号原子引用import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;public class ABADemo {static AtomicReferenceInteger atomicReference new AtomicReference(100);static AtomicStampedReferenceInteger atomicStampedReference new AtomicStampedReference(100, 1);public static void main(String[] args) {System.out.println(以下是ABA问题的产生);new Thread(() - {atomicReference.compareAndSet(100, 101);atomicReference.compareAndSet(101, 100);}, t1).start();new Thread(() - {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicReference.compareAndSet(100, 2019) \t 当前值 atomicReference.get());}, t2).start();System.out.println(以下是ABA问题的解决);new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() \t第1次版本号 stamp);// t3线程暂停1stry {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第2次版本号 atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() 1);System.out.println(Thread.currentThread().getName() \t第3次版本号 atomicStampedReference.getStamp());}, t3).start();new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() \t第1次版本号 stamp);// t4线程暂停3s保证t3线程完成ABAtry {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());System.out.println(Thread.currentThread().getName() \t当前实际最新值 atomicStampedReference.getReference());}, t4).start();}
}