Cache(三级缓存)的出现是为了解决cpu(贼快)和内存(相比cpu慢的多,但是redis也是基于内存的数据库,一旦程序关闭(也就是redis退出),那么内存中存储的缓存都会消失,而redis在启动时通过aof或rdb将数据重新读取到内存中来,扯远了。。。。。。)之间读写速度严重不统一的问题
CPU缓存一致性问题:i++
首先,在程序运行期间,将主内存中的数据复制一份存储到CPU Cache中,那么cpu寄存器在进行数值计算的时间就直接到Cache中进行读写,等到整个过程结束,再将Cache中的数据刷新到主存中。但是在多线程的情况下:这很容易造成数据不一致
Java内存模型
假设主内存给共享变量为0,线程1和线程2分别共享变量X的副本,假设线程1此时将工作内存中的x修改为1,同时刷新到主内存中(但是这个刷新时间并不确定),若线程2想去使用副本x的时候,会发现变量已经失效,必须到主内存中再次获取存入到自己的工作内存中,这点与CPU和CPU Cache之间的关系非常相似。看起来似乎没什么问题,但是若此时两个线程都同时修改自己工作内存为1,然后先后刷新主内存,那么就会造成数据不一致
JVM的读取,赋值操作都是原子性的不可被中断的,但是volatile关键字不具备原子性的语义
volatile关键字:一个线程修改了volatile修改的变量,另外一个线程会立即看到修改的最新的值:
比如:
- reader线程从主内存中获取值为0,将其缓存到本地内存中
- update线程将自己的本地内存的值修改为1,然后立即刷新到主内存(注意:这里是立即刷新,那为什么还不能保证原子性呢?看下面解析)
- reader看到自己本地内存的值失效(因为刷新后,会去通知拥有副本的值都失效),因此到主内存中重新读取值
这样就保证了可见性
但是i++并不是原子性的,若此时两个线程都同时修改自己工作内存为1,然后先后刷新主内存,就会导致数据不一致
volatile和synchronized的区别
- volatile只能修饰变量
- synchronized只能修饰方法,语句块
- volatile修饰的变量可以为null,synchronized关键字修饰的同步语句块的monitor对象不能为null