在发生并发的时候,主要需要考虑三个方面:原子性、有序性、可见性。
原子性:
我在做某件事情的时候,不允许插队或者不允许修改与这件事情相关的东西。如何保证原子性呢,把并行改为串行就可以,加锁。
有序性:
在多线程的情况下,代码的执行顺序可能会发生变更
在单线程的时候,不会影响最终结果
可见性:
线程启动后,对线程外改的变量。对于线程不可见。
1. MESI的由来-有序性和可行性的产生
从cpu讲起,为了提高访问效率,某个请求都不是从cpu主内存拿取,而是通过缓存拿,提高了效率但存在缓存一致性问题,cpu为了保证缓存数据和主内存数据一致,有想了一些办法保证数据一致性,通过总线程+mesi协议保证数据一致性。
每个cpu都会从主内存中拿数据,红线总线程表示cpu拿主线程拿的时候,必须要一个一个拿,是串行的。
mesi协议表示的是四个状态,
M是被修改的状态,当前缓存的数据与主内存的数据是不一致的,并且只缓存在当前的cpu
E是独享状态,数据只缓存在当前cpu,并且没有被修改
S是共享的状态,代表这个数据缓存在多个CPU,并且和主内存的数据一致
I是无效的状态,代表这个数据在当前CPU无效
memory是主内存,这三个黑线理解成总线,总线主要是为了cpu与主内存,cpu与cpu通讯的一个总线。CACHE是缓存。
通过总线和mesi协议串行执行又导致性能变慢了,因为mesi操作有很多的通知流程,cpu是等待的,那么cpu在等待过程中不能做任何事,这样就很不合理,所以cpu的作者就考虑这个等待其它cpu结束能不能异步去做,你通知你的,我cpu继续执行后续逻辑,通知完了以后,你通知完后就告诉cpu就好了。对于这个过程又引入了两个东西:stroe buffer cpu1判断总线上的任务有没有执行完成,如果没有就去和其他cpu交互,当总线执行完数据处理再将cpu1的结果执行到总线中,进行数据的保存或修改。invaliadate queue无效队列 如果其他cpu进行了修改,并且通知当前cpu的数据为无效的,状态变为i。cpu会慢慢将数据同步到cache缓存中。
cpu的异步处理导致了数据不一致问题,cpu不解决但提供方案,用volatile,使用volatile的话会导致store buffer 和invaliadata 失效,volatile可以解决有序性和可见性的问题。
2. Happerns-before规则
volatile规则:
程序顺序规则、start规则、监视器锁规则