前言:
今天被al小哥哥问了几个问题,回答的问题在很大程度上代表你对这个问题的深刻理解。然而 自己...真的真的 没有把一些东西真正的理解透啊。
知识点不在于 看了多少遍,在于不断的 系统的去总结、分析、理解。
本着对volatile的理解,提出来几个问题?
1.volatile怎么实现的可见性?-->MESI协议
2.cas怎么实现的可见性? -->也是用的MESI 协议
3.volatile避免指令的重排序是通过内存屏障解决的,那他还和MESI有什么关系?MESI也用了内存屏障?
1.1volatile:(可见性、有序性)注:对于单次的volatile变量操作是原子性的,但是连续对volatile操作不是原子性的
一:对于一个volatile变量,写进去的时候 会把工作内存里面的 值刷新到 主内存里面;而 读volatile变量的时候 ,如果发现这个变量是一个volatile变量的话,会使这个变量失效,重新再从主内存里面读取进来。这个回答是不是太.....肤浅。
《java并发编程艺术》说,volatile在编译的时候会生成一个lock前缀。然而 lock前缀到底有什么作用呢?对于此理解的真的不够深刻啊。 多线程编程,在操作系统里面是用户态的,通过操作系统的 用户态线程和内核线程之间的对应,可以将用的线程 映射到其它处理器上面去,(其实线程内的工作内存是 cpu寄存器和cpu缓存的抽象)(那么问题出现了 --------》
如果一个线程A在cupu1上面,线程B在cpu2上面,假如 cpu1和 cpu2同时更改变量X,两个处理器各自执行各自的,然后放到各自的缓存在继续执行下条指令,那么 不是乱套了么?所以,必须想办法 只能有一个处理器在操作 一个共享变量,而让另一个处理器中缓存了该变量的值无效!!所以前辈们想出了两个方案:
1.锁住总线:当一个处理器输出一个信号(Lock#),然后别的处理器就不能访问共享变量。 这个方法简单粗暴啊。所以人们肯定得想个别的办法把。
2.锁住缓存:大白话的思想就是 cpu1 操作了一个共享内存数据,同时通知其它的cpu,让他们的对于这一个共享内存的缓存无效!如果其它cpu在要操作这个共享内存的数据的话,得重新读缓存。
说实话《java并发编程艺术》对于缓存一致性协议讲的有点生涩,看了很多遍对于这一概念还是模糊,于是找度娘把
---->缓存一致性协议:MESI 协议,就是给各个处理器制定了一套游戏规则:
M (modeified) 被修改的: 处于这个状态的数据只存在当前处理器的缓存当中,并且改变了原来的值
E(exclusive) 独占的: 处于这个状态的数据只存在当前处理器的缓存当中,并且和内存中的值 一样,没有被修改
S (shared)共享的: 处于这个状态的数据在多个cpu缓存中的值一样,并且和内存中的值一样
I (invalid)无效状态: 处于贝格cpu中的缓存已经无效。
如果当前cpu要读数据了,如果是I状态,那么读进数据来之后,把状态改成S。其它的状态可以直接读就可以,在此之前,必须监听一下别的cpu缓存对于此数据的状态,如果别的cpu存在M状态,那就等那个cpu把数据存到内存中然后再去读。
如果说当前cpu要写数据了,只有M 和E 状态才能执行,否则发出一条指令,至其他的cpu的 缓存置为无效,然后把状态改为M.
再回到上文,volatile在总线上发出的lock前缀,会声言出lock#信号,最近的lock# 信号使用的是所缓存的。所以lock前缀什么作用,我们可以总结成两点
1.让该缓存行刷新到内存中去
2.使其它处理器的缓存的数据无效
1.2:CAS
/*
如果两个处理器,同时对一个数据进行CAS,那么会怎么做?同时对一个数据进行CAS,也就是说要更改本处理器中缓存的数据,更改状态为M或者E,成功了之后是其他的处理器中的数据无效 变成I. 然而怎么决定让哪一个处理器更改缓存数据呢? 这个就让ring bus仲裁吧。
*/
2:内存屏障
硬件层 两个内存屏障 load barrier 、store barrier
有两个功能:1、禁止屏障前后的重排序 2、强制把写缓冲区的的数据写入内存
LoadStore屏障: (Load1 LoadStore Store2) 在store2以及后续的刷入内存前,先进行Load1把数据读出来
LoadLoad屏障:(Load1 LoadLoad Load2)在Load1以及后续读数据前,先进行Load1把数据读出来
StoreLoad屏障(万能屏障):(Store1 StoreLoad Load2)在Load2读数据之前,先进行store1写操作,使其对所有处理器可见
StoreStore屏障:(Store1 StoreStore Store2)在store2及后续写操作前,先进行store1写操作,使其对所有处理器可见
volatile的内存屏障:
volatile写操作: 前面插一个storestore屏障 ; 后面插一个storeload屏障
volatile读操作: 后面插一个loadload屏障,后面插一个loadstore操作
3.happens-before
四个规则:
程序顺序规则:一个线程中的每个操作都happends-before于后续操作
监视器锁原则:对一个锁的解锁 happends-before 后续对这个锁的加锁
volatile变量原则:前一个volatile变量的写操作happends-before后面的读操作
传递性原则:Ahappens-beforeB, Bhappends-beforeC,那么Ahappends-beforeC
注解:任何带有lock前缀的指令都有 内存屏障的作用
所以,还是静下心,调整一下心态,这几天确实被春招弄的有点浮躁了。没有根基的高楼注定是虚幻的,所以基础真的很重要呀
以上笔记如果有错欢迎指正呀~~~