缓存模型
在计算机,所有的运算操作都是通过CPU的寄存器来完成的,
涉及到数据的读写操作,都需要从计算机主存中获取。
由于主存的读写效率不高,出现了缓存模型,
缓存的出现可以解决CPU直接访问内存效率低下的问题。
在程序运行的过程中,会将需要的数据从主存中复制一份到缓存中,
让CPU在缓存中读写数据,运算结束后,再将缓存中的数据刷新到主存中。
缓存一致性问题
缓存的出现,极大的提高了CPU的吞吐能力,但是也引入了缓存不一致的问题。
比如i++操作
1)读取主内存中的i到缓存中
2)对i进行加1操作
3)将结果写回缓存
4)将数据刷新到主内存
这样的操作在多线程下就会出现问题,每个线程都有自己的工作内存
(对应于CPU中的缓存),变量i在对个线程的本地内存中都有副本。
两个线程同时执行i++,可能会出现两次自增结果还是1的问题
Java的内存模型
共享变量存储于主内存,每个线程都可以访问
每个线程都有自己的本地内存
本地内存只存储该线程对共享变量的副本
线程不能直接操作主内存
并发编程的三个重要特性
原子性:一次或者多次操作,要么全部执行,要么都不操作
可见性:当一个线程对共享变量进行了修改,另外的线程可以立即看到修改后的值
有序性:程序代码在执行过程中的先后顺序
多个原子性的操作在一起就不是原子性操作
Java内存模型只保证了基本读取和复制的原子性操作
Volatile不保证原子性,可以使用synchronized
Private volatile int i = 1;
进行i++操作,由于volatile不保证原子性,所有多线程还是会出现问题
可以使用AtomicInteger来代替
Volatile可以保证可见性:
使用volatile修饰变量,
对于共享资源的读取会直接在主内存中进行(也会复制到工作内存,
其它线程对该共享资源修改之后,会导致当前线程在工作内存中的共享资源失效,
必须从主内存中从新获取),对共享资源的写操作会立即刷新到主内存
Volatile可以保证有序性:
禁止对指令进行重排序操作来保证有序性
使用volatile修饰的变量,对一个变量进行写操作要早于对这个变量的读操作
Volatile修饰的变量:保证了不同线程之间对共享变量操作时的可见性,
也就是一个线程修改volatile修饰的变量,另外一个线程会立即看到最新的值
Volatile和Synchronized的区别
Volatile只能修饰实例变量或者类变量
Synchronized只能用于修饰方法和语句块
Volatile不能保证原子性
Synchronized可以保证原子性