Java_volatile_CAS_ABA

1、JMM (java 内存模型)

  • 描述程序中各变量(实例字段、变量、静态变量、数组、对象)的访问方式。
  • 所有变量放在主内存中,每个线程有自己的工作内存,线程操作变量时,把变量拷贝到自己的工作内存中修改,修改完后在同步到主内中
  • 可见性(利用volatile)
  • 原子性(利用sysnchronized)
  • 有序性——指令重排:
    • 计算机在执行程序时为了提高性能,编译器和处理器常常会对指令做重排。
    • 单线程环境下,最终执行结果与代码顺序一直。
    • 处理器进行指令重排时必须考虑指令之间的数据依赖性。
      • int a = 1; a=1+2; (必须先执行第一行声明a--数据依赖性)
    • 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保持一致是无法确定的

2、volatile  简单介绍

volatile int a;

volatile是java虚拟机提供的轻量级同步机制,轻量级的synchronized,

  • 保证可见性(a的值发生了改变,其他 线程会马上看到)
  • 禁止指令重排(让编译器不对指令重排,按照自己定义的属性执行)
  • 不保证原子性(使用原子类AtomicInteger解决)

 

3、CAS:比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令

Unsafe + cas思想(自旋锁)

cas是什么?

  • 是一条cup并发原语句(执行必须是连续的, 在执行中不允许被打断)
  • 判断某个物理位置的值与期望值是否相等,如果是则改变(是原子操作,不会造成数据不一致的问题)。

unsafe类:

是cas 的核心类,由于java方法不能直接访问底层系统,需要通过本地native方法,Unsafe类路径: sun.misc.Unsafe.class

代码:

//代码使用
AtomicReference atomicReference = new AtomicReference(TestAA);
		atomicReference.compareAndSet(new TestAA(), null);
		
int a = 2;
AtomicReference atomicReference2 = new AtomicReference(a);
//如果a的值=2 就改成 3,否则返回fales 不修改
System.out.println(atomicReference2.compareAndSet(2, 3)+"\t"+atomicReference2.get());


//atomic--cas流程源码查看
AtomicInteger acit = new AtomicInteger();
acit.getAndIncrement();//+1

/**
    getAndIncrement()源码
*/
public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);//当前对象,地址偏移量,+1
}

/**
getAndAddInt 源码
自旋锁
*/
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt)
  {
    int i;
    do
    {
      i = getIntVolatile(paramObject, paramLong);
    } while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));
    //compareAndSwapInt比较与交换
    return i;
  }

/**
paramObject:当前对象
paramLong:引用地址
paramInt1:要改变的数
paramInt2:与当前对象的值比较,相同替换,如果不同,继续取值然后再比较,直到更新完成
*/
public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2     );

4、ABA问题

int a=1;

aba问题是,线程1把改成了2(a=2),同时线程2获取到a变量改成3(a=3)还没有提交,线程1又改成了a=1,这时线程2改完提交。(ABA问题)尽管最后结果成功,但并不代表这个过程就没有问题

解决:AtomicStampedReference 使用版本号

AtomicStampedReference stampedReference = new AtomicStampedReference(a, 1);
		//期望值,新值,切望版本号,新版本号
		stampedReference.compareAndSet(expectedReference, newReference, stampedReference.getStamp(), newStamp)

 

你可能感兴趣的:(java)