DecadeAnkle / JavaStudyRecord

Java学习内容汇总
0 stars 0 forks source link

Java并发编程的艺术-深入理解volatile语义 #5

Open DecadeAnkle opened 8 months ago

DecadeAnkle commented 8 months ago

volatile的内存语义

一个volatile变量的单个读/写,与一个普通变量的读/写操作用同一个锁来同步,它们之间的效果相同,看示例

class VolatileFeaturesExample{
    volatile long v = 0L;

    public void setV(long l) {
        this.v = l;
    }

    public void getAndIncrement(){
        this.v++;
    }

    public long getV() {
        return v;
    }
}
class VolatileFeaturesExample{
    volatile long v = 0L;

    public synchronized void setV(long l){
        this.v = l;
    }

    public void getAndIncrement(){
        long temp = getV();
        temp += 1L;
        setV(temp);
    }

    public synchronized long getV() {
        return v;
    }
}

简而言之,volatile变量有这两个特性

  1. 可见性:对一个volatile变量的读,任意线程都可以看到对这个volatile变量的最后写入。
  2. 原子性:对任意单个volatile变量的读/写具有原子性,但是类似于volatile++这种操作不具有原子性。

volatile的写-读内存语义和实现

  1. 当写一个volatile变量时,Java内存模型会把该线程对应的本地内存中的共享变量刷新到主内存
  2. 当读一个volatile变量时,Java内存模型会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量

为了实现volatile写/读的内存语义,Java内存模型会限制重排序的类型,下面是Java内存模型针对编译器指定的volatile重排序规则表 6e6993c55e911fdfd1f1648d6b3d73f

为了实现volatile内存语义,编译器在生成字节码的时候,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

具体示例

class VolatileBarrierExample{
    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    void readAndWrite(){
        int i = v1;    //第一个volatile读
        int j = v2;    //第二个volatile读
        a = i + j;     //普通写
        v1 = i + 1;   //第一个volatile写
        v2 = j + 1;   //第二个volatile写
    }
}

b3d0688f3a873de7b89093840c2fd1a

内存屏障含义 6440e0771671ab7025d5bf3d0794698