内存屏障:
内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:
1、保证特定操作的执行顺序。
2、影响某些数据(或则是某条指令的执行结果)的内存可见性。
编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。
Memory Barrier所做的另外一件事是强制刷出各种CPU cache,如一个 Write-Barrier(写入屏障)将刷出所有在 Barrier 之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。
内存屏障.png这和java有什么关系?volatile是基于Memory Barrier实现的。
如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。
volatile.png
这意味着,如果写入一个volatile变量a,可以保证:
1、一个线程写入变量a后,任何线程访问该变量都会拿到最新值。
2、在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。
总结:
特性一:保证变量在线程之间的可见性,可见性是基于CPU内存屏障指令,被JSR-133抽象为happens-before原则
特性二:阻止编译时和运行时的指令重排,编译时JVM编译器遵循内存屏障的约束,运行时靠CPU屏障指令来阻止重拍
不能实现原子性,
例如:
开启十个线程,每个线程中让静态变量count自增100次,最后的结果未必是1000,有可能少于1000
因为count++操作不是原子性操作,
原子性操作就是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch
在多进程访问共享资源时,能够确保所有其他的进程(线程)都不在同一时间内访问相同的资源。原子操作(atomic operation)是不需要synchronized,这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度,机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。通常所说的原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。
一个变量在能够用volatile变量修饰的前提条件是
1.运行结果不依赖变量的当前值
2.变量不需要与其他状态的变量共同参与不变约束
本文参考 :https://www.jianshu.com/p/d3fda02d4cae 占小狼
网友评论