java可见性问题、内存屏障、volatile关键字的理解

目录

    • 前言
    • 指令重排序
    • 内存屏障
    • volatile

前言

我们先来看一段代码

public class Test1 {

    static boolean stop = false;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while(true) {
                if(stop) {
                    return;
                }
            }
        });
        t1.start();
        Thread.sleep(1000);
        stop = true;
    }
}

这段代码主线程阻塞一秒后修改了stop的值为true,理论上线程会结束while循环,但是事实上这段程序永远也不会停止

原因如下:

硬件层面,CPU的高速缓存,CPU默认会存在三级缓存

即当CPU核心A、B加载stop时会先把stop的值加载到核心的高速缓存,当A修改值为true时并不会马上把stop的值刷新到主内存,CPU会在适当的时机把值刷新到内存,这个时间是不确定的。核心B由于自己的高速缓存中存在stop的值,所以即使主内存的值更新了,核心B仍然只会读取自己缓存中的值。这就是可见性问题,造成可见性问题的原因也是在于CPU为了提高CPU的处理效率,所以CPU会提供内存屏障来供我们自己解决业务上的不可见问题

指令重排序

public class Test1 {
    int a = 0;
    int b = 0;
    
    void set() {
        a = 1;
        b = 1;
    }

    void get() {
        while(b == 1) {
            assert (a == 1); // 1 可能抛出异常
        }
    }
}

这段代码在位置1出可能会抛出异常,即当b==1时a可能不等于1

原因如下:
CPU在单线程条件下不影响主流程结果的情况下是可以优化我们的指令的,即可能会重排序我们的指令,即a=1;b=1;可以被CPU指令重排序为b=1;a=1;

内存屏障

内存屏障是硬件层面提供给我们的可以禁止指令重排序的功能

X86的memory barrier指令包括lfence(读屏障) sfence(写屏障) mfence(全屏障)

  • Store Memory Barrier(写屏障) ,告诉处理器在写屏障之前的所有已经存储在存储缓存(store
    bufferes)中的数据同步到主内存,简单来说就是使得写屏障之前的指令的结果对屏障之后的读或 者写是可见的
  • Load Memory Barrier(读屏障) ,处理器在读屏障之后的读操作,都在读屏障之后执行。配合写屏
    障,使得写屏障之前的内存更新对于读屏障之后的读操作是可见的
  • Full Memory Barrier(全屏障) ,确保屏障前的内存读写操作的结果提交到内存之后,再执行屏障 后的读写操作

volatile

在java层面提供了volatile关键字,volatile修饰一个变量时即可在关键位置创建屏障来阻止指令重排序

如下代码

public class Test1 {
	// 修饰变量a
    volatile int a = 0;
    int b = 0;
    
    void set() {
        a = 1;
        // storeMemoryBarrier()写屏障,写入到内存(当a写入之后会创建写屏障)
        b = 1;
    }

    void get() {
        while(b == 1) {
        	// Load Memory Barrier()读屏障,在读取a之前从主内存中获取a的值
            assert (a == 1); 
        }
    }
}

通过前面的内容分析我们发现,导致可见性问题有两个因素,一个是高速缓存导致的可见性问题,
另一个是指令重排序。

volatile的主要作用

  • 使用内存屏障阻止指令重排序
  • 使用内存屏障,写屏障把数据写入到主内存,读屏障直接从主内存获取值

即使用volatile即可解决可见性问题

你可能感兴趣的:(java,多线程,可见性,volatile,内存屏障)