摘要

咱们这一讲次要解说基于volatile实现并发:可见性跟有序性问题,解说volatile的时候,须要解说:cpu缓存模型 -> java内存模型 -> 并发编程3大个性:原子性、可见性、有序性 -> volatile的作用 -> volatile的底层原理 -> volatile实战。

思维导图

内容

cpu多级缓存模型

1.volatile引入

咱们先看下以下例子:

public class VolatileTest {     static int flag = 0;    public static void main(String[] args) {        /**         * 1、开启一个读线程,读取flag的值         */        new Thread(){            @Override            public void run() {              int localFlag = flag;              while (true){                 if(localFlag != flag){                      System.out.println("读取到的标识位值:"+flag);                      localFlag = flag;                  }                  try {                      TimeUnit.SECONDS.sleep(2);                  } catch (Exception e) {                      e.printStackTrace();                  }              }            }        }.start();        /**         * 2、开启一个读写线程,批改flag值         */        new Thread(){            @Override            public void run() {                int localFlag = flag;                while (true){                    System.out.println("标识位被批改了:"+ ++localFlag);                    flag = localFlag;                    try {                        TimeUnit.SECONDS.sleep(2);                    } catch (Exception e) {                        e.printStackTrace();                    }                }            }        }.start();    }}

后果输入为:

通过以上咱们发现:
1、下面代码实例中:flag是动态成员变量,存在与办法区;此办法区外面的资源是线程共享的资源,线程共享资源在多线程数据读取跟批改时候,会呈现线程平安问题。而localFlag是办法栈外面的变量是线程公有的数据。每次是把堆外面的数据读取进去赋值给栈外面的数据。
2、共享数据在多线程读写时候,会呈现线程不一致性问题;读线程不可能精确读取到写线程批改的数值。
3、在理论的零碎运行过程中,可能会产生一个问题,就是说,Thread1批改变量的值,他批改了这个变量的值,后果呢,发现Thread2在他批改变量值之后,没那么快能感知到flag数值的变动。Thread1,曾经将flag设置好了;然而Thread2,比如说在一段时间范畴内,还是读到了旧的flag,在一小段时间范畴内,可能Thread2会感知不到Thread1对flag的值批改,他读到的可能还是flag的这么一个旧的值。

咱们将变量加上volatile润饰

 static volatile int flag = 0;

输入后果如下:

咱们发现加上volatile之后,每次读线程都可能精确获取到volatile润饰的数据。

并发编程中:你只有开了多个线程,肯定会有一些这种问题,某个线程批改一个变量值,其余线程要立即感知到这个变量值的变动,然而如果你不必volatile,会有问题:有线程批改了一个变量的值,后果其余的线程感知不到这个变量值的批改

volatile:并发编程中,一个线程批改了某个共享变量值,其余线程会立即感知到这个变量值的变动。volatile只是保证数据可见性,有些人说他是一个轻量级锁,其实是打错特错的。

2.cpu多节缓存模型

现实中的cpu模型:

如果咱们的cpu内存模型如上:第二个cpu进行数据的读写,第一个cpu进行数据读取,每次第二个cpu会先从主存读取数据,而后进行写操作,将不会存在数据不统一问题,因为主内存外面的数据都是最终的数据。每次cpu1都是读取的主内存外面数据。每次读取时候,只有主内存外面数据更改了,cpu1就可能读取到最新的值。

古代计算机cpu内存模型因为:内存的读取速度跟不上cpu的处理速度。所以引出了;内存的读写速度没什么冲破,cpu如果要频繁的读写主内存的话,会导致性能较差,计算性能就会低。所以引出了:cpu多级缓存模型:


1、当初计算机为了均衡主内存跟cpu处理速度协调问题,在其之间减少了多级cpu缓存。
2、cpu能够间接操作本人对应的cpu缓存,不须要间接频繁的跟主内存通信,这个是古代计算机技术的一个提高,这样能够保障cpu的计算的效率十分的高。
3、这样的cpu多级缓存模型将会导致主内存外面的实时数据在其余cpu线程外面读取不到,会有一个提早。

cpu多级缓存导致数据不统一问题的剖析。

3.总线加锁机制和MESI缓存一致性原理

晚期数据不统一问题应用总线加锁机制保障。
总线加锁机制:
原理: 某个线程批改主内存外面共享数据的时候,会通过一个总线,将共享数据加锁,而后这个线程执行完相应操作之后才会开释锁。
问题: 效率低下:总线加锁机制使得多线程串行化执行,多线程下效率低下。

目前比拟罕用的是缓存一致性原理:MESI
MESI缓存一致性原理:
原理: 强制刷新主内存+cpu嗅探机制:某一个线程批改某一个数值时候,会将此数值强制刷新到主内存,而后应用cpu的嗅探机制公布一个批改数据的事件,而后其余线程嗅探到此数据被批改,而后使本地cpu缓存数据过期,而后把从主内存外面从新加载。
底层原理: lock前缀指令+内存屏障