我个人总结理解的两方面影响:
简单举例说明,就不用看下面详细解释了:
i=0; A,B2个线程各i++ 50000次
1.线程A 读到i=0时,A阻塞,B也读到i=0,进行i++ 写到主存,此时i=1,A继续执行进行i++,i=1写回主存。
2.MESI协议中,AB线程同时进行修改i,此时裁决机制进行裁决设缓存A为M状态,缓存B为I状态,那么B线程修改无效,并且浪费一次,A进行i++把i=1写回主存。
第一方面分别执行三次,每次结果各不相同:
输出最后结果:93873
输出最后结果:80319
输出最后结果:95797
假设线程A首先读取到了i的0,但是还没来得及修改,就阻塞了,这时线程B开始了,他也读取到i的值,由于i的值未被修改,即使是被volatile修饰,主存的变量还没变化,那么线程B得到的值也是0,之后对其进行加1 *** 作,得到i=1后,将新值写入到缓存中,再刷入主存(此时主存i由0变为0)中。
此时如果A线程被唤醒,A线程将继续执行加1 *** 作,得到i=1,将新值写入到缓存中,再输入到主存中(此时主存i由1变为1),总共100000次 *** 作浪费了一次i++,多次线程竞争阻塞导致多次浪费,所以达不到预期100000
第二方面因为volatile底层使用的是缓存一致性协议,会根据MESI原则。
MESI分别代表缓存行数据所处的四种状态,通过对这四种状态的切换,来达到对缓存数据进行管理的目的。
M 修改 Modified : 该缓存行有效,且该缓存行数据被修改了,和内存中的数据不一致,数据只存在于本缓存行中,此时状态为M ;
E 独占 Exclusive : 该缓存行有效,且该缓存行数据和内存中的数据一致,此时状态为E;
S 共享 Shared : 该缓存行有效,且该缓存行数据和内存中的数据一致,数据同时存在于其他缓存中(同步到主内存中完成后 , 标记为共享状态 ; 共享状态的变量才能被线程加载到工作内存中),此时状态为S ;
I 失效 Invalid : 该缓存行无效,线程 A , B 中都使用了同一个共享变量 , 如果在线程 A 中修改该变量 , 线程 B 中的变量更新前都标记为失效状态,此时A状态为M状态,B状态为I状态 ;
1、CPU1从内存中将变量i加载到缓存中,并将变量i的状态改为E(独享),并通过总线嗅探机制对内存中变量i的 *** 作进行嗅探(因为有volatile关键字修饰,如果没有修饰则不会进行嗅探,说明可见性)
2、此时,CPU2读取变量i,总线嗅探机制会将CPU1中的变量i的状态置为S(共享),并将变量i加载到CPU2的缓存中,状态为S
3、CPU1对变量i进行修改 *** 作,此时CPU1中的变量i会被置为M(修改)状态,而CPU2中的变量i会被通知,改为I(无效)状态,此时CPU2中的变量i做的任何修改都不会被写回内存中(高并发情况下可能出现两个CPU同时修改变量i,并同时向总线发出将各自的缓存行更改为M状态的情况,此时总线会采用相应的裁决机制进行裁决,将其中一个置为M状态,另一个置为I状态,且I状态的缓存行修改无效)
4、CPU1将修改后的数据写回内存,并将变量i置为E(独占)状态
5、此时,CPU2通过总线嗅探机制得知变量i已被修改,会重新去内存中加载变量i,同时CPU1和CPU2中的变量i都改为S状态
这就是完整的MESI流程,因为第3步中,高并发竞争总线会采用相应的裁决机制进行裁决,将其中一个置为M状态,另一个置为I状态,且I状态的缓存行修改无效,浪费一次i++,所以i也达不到100000。
有上述两点共同作用导致volatile的原子性无法保证,如果加锁,可完全避免上述两点情况。
最后,如果文章有问题留言反馈,会及时更改,以免误导他人,
如果觉得有帮助,请帮忙点个赞!感谢!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)