MESI分别代表缓存行数据所处的四种状态,通过对这四种状态的切换,来达到对缓存数据进行管理的目的 。
状态描述监听任务M 修改(Modify)该缓存行有效,数据被修改了,和内存中的数据不一致,数据只存在于本缓存行中缓存行必须时刻监听所有试图读该缓存行相对应的内存的操作,其他缓存须在本缓存行写回内存并将状态置为E之后才能操作该缓存行对应的内存数据E 独享、互斥(Exclusive)该缓存行有效,数据和内存中的数据一致,数据只存在于本缓存行中缓存行必须监听其他缓存读主内存中该缓存行相对应的内存的操作,一旦有这种操作,该缓存行需要变成S状态S 共享(Shared)该缓存行有效,数据和内存中的数据一致,数据同时存在于其他缓存中缓存行必须监听其他缓存是该缓存行无效或者独享该缓存行的请求,并将该缓存行置为I状态I 无效(Invalid)该缓存行数据无效无而MESI协议是通过总线嗅探技术实现的:
总线嗅探是通过CPU侦听总线上发生的数据交换操作,当总线上发生了数据操作,那么总线就会广播对应的通知,CPU收到通知后,再根据本地的情况进行响应 。
5. 有序性问题虚拟机在进行代码编译时,对改变顺序后不会对最终结果造成影响的代码,虚拟机不一定会按我们写的代码顺序运行,有可能进行重排序 。实际上虽然重排后不会对变量值有影响,但会造成线程安全问题 。
重排序又可以分为三种:
- 编译器优化的重排序 。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
- 指令级并行的重排序 。现代CPU采用了指令级并行技术来将多条指令重叠执行 。对于不存在数据依赖的指令,CPU可以改变语句对应机器指令的执行顺序
- 内存系统的重排序 。由于CPU使用三级缓存结构,这使得数据加载和存储操作看上去可能是在乱序执行的
happens-before由以下八大原则组成:
- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作(线程的执行结果有序)
- 锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作
- volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作
- 传递规则:如果操作A先行发生于操作B,操作B先行发生于操作C,则可以得出操作A先行发生于操作C
- 线程启动规则:Thread对象的start()方法先行发生于该线程的其他任何操作
- 线程中断规则:对线程中断方法interrupt()的调用先行发生于被中断线程检测到中断事件的发生
- 线程终结规则:线程中所有操作先行发生于线程的终止检测 。通过Thread.join()方法结束、Thread.isAlive()方法的返回值等手段检测到线程已经终止执行 。比如在A线程中调用B.join()方法,B线程执行完成后,B对共享变量的修改,对A来说是可见的
- 对象终结规则:一个对象的初始化方法完成先行发生于该对象的finalize()方法的开始
而volatile是通过插入内存屏障(Memory Barrier),在内存屏障前后禁止重排序优化,以此实现有序性 。
经验总结扩展阅读
- pytorch、paddlepaddle等环境搭建 深度学习环境搭建常用网址、conda/pip命令行整理
- 6 Java多线程:锁与AQS(下)
- 三十九 Java开发学习----SpringBoot整合mybatis
- JavaSPI详解
- day04-JavaScript01
- Java安全之Tomcat6 Filter内存马
- JavaScript的异步编程之Promise
- 5 Java多线程:CAS
- java中HashMap的设计精妙在哪?
- JAVA系列之JVM内存调优