一.happens-before原则
同一个线程中的,前面的操作 happens-before 后续的操作。(即单线程内按代码顺序执行。但是,在不影响在单线程环境执行结果的前提下,编译器和处理器可以进行重排序,这是合法的。换句话说,这一是规则无法保证编译重排和指令重排)。
1. 监视器上的解锁操作 happens-before 其后续的加锁操作。(Synchronized 规则)
2. 对volatile变量的写操作 happens-before 后续的读操作。(volatile 规则)
3. 线程的start() 方法 happens-before 该线程所有的后续操作。(线程启动规则)
4. 线程所有的操作 happens-before 其他线程在该线程上调用 join 返回成功后的操作。
5. 如果 a happens-before b,b happens-before c,则a happens-before c(传递性)
二.volatile关键字的作用
Volatile关键字主要用于保证内存可见性和顺序性:
1.volatile修饰的变量可以保证一个线程对该变量的写happens-before 一个线程对该变量的读
2.Volatile可以用来修饰long和double类型的变量使其以原子方式执行
3.Volatile在双重检查加锁的单例模式中的应用,可以防止重排序
三.volatile关键字的实现原理
在java代码编译成字节码时会在volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令。
我们知道为了提高处理速度,CPU不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完不知道何时会写回内存。如果对声明了volatile的变量进行写操作,JVM就会向CPU发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所以,在多处理器下,为了保证各个CPU的缓存是一致的,就会实现缓存一致性协议,每个CPU通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前CPU的缓存行设置成无效状态,当CPU对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里
- Lock前缀的指令会引起CPU缓存写回内存;
- 一个CPU的缓存回写到内存会导致其他处理器的缓存失效;
- 当CPU发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值。
这样针对volatile变量通过这样的机制就使得每个线程都能获得该变量的最新值
volatile相关的理解就写到这里,如果大家还想详细的了解volatile可以查阅相关资料或者私信我进行交流。
并发是java中无法避免的问题,基本上java面试都会涉及一些并发相关的问题,尤其是互联网公司一定会问到,今天就先写到了这里,下次准备把并发中相关的锁和并发集合的知识整理一下分享给大家,如果喜欢记得关注我【不爱八阿哥】,有什么问题也可以私信交流,让我们共同进步。
来源:https://www.toutiao.com/i6656572928701760004/