volatile是一个轻量级的synchronized,一般作用于变量,在多线程开发中保证了内存的可见性。相比于synchronized关键字,volatile关键字的执行成本更低,效率更高。
多线程三大特性:原子性、可见性、有序性;
volatile具有三大特性:
volatile可以保证不同线程对共享变量进行操作时的可见性,即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。
导致内存不可见的主要原因就是Java内存模型中的本地内存和主内存之间的值不一致所导致,例如线程A访问自己本地内存A的X值时,但此时主内存的X值已经被线程B所修改,所以线程A所访问到的值是一个脏数据。
volatile可以保证内存可见性的关键是volatile的读/写实现了缓存一致性,缓存一致性的主要内容为:
每个处理器会通过嗅探总线上的数据来查看自己的数据是否过期,一旦处理器发现自己缓存对应的内存地址被修改,就会将当前处理器的缓存设为无效状态。此时,如果处理器需要获取这个数据需重新从主内存将其读取到本地内存。·当处理器写数据时,如果发现操作的是共享变量,会通知其他处理器将该变量的缓存设为无效状态。
那缓存一致性是如何实现的呢?可以发现通过volatile修饰的变量,生成汇编指令时会比普通的变量多出一个Lock 指令,这个Lock指令就是volatile关键字可以保证内存可见性的关键,它主要有两个作用:
1.将当前处理器缓存的数据刷新到主内存。
2.刷新到主内存时会使得其他处理器缓存的该内存地址的数据无效。
volatile通过禁止指令重排来保证代码的执行顺序。
为了实现volatile的内存语义,编译器在生成字节码时会通过插入内存屏障来禁止指令重排序。
内存屏障:内存屏障是一种CPU指令,它的作用是对该指令前和指令后的一些操作产生一定的约束,保证一些操作按顺序执行。
- class VolatoleAtomicityDemo {
- public volatile static int inc = 0;
-
- public void increase() {
- inc++;
- }
-
- public static void main(String[] args) throws InterruptedException {
- ExecutorService threadPool = Executors.newFixedThreadPool(5);
- VolatoleAtomicityDemo volatoleAtomicityDemo = new
- VolatoleAtomicityDemo();
- for (int i = 0; i < 5; i++) {
- threadPool.execute(() -> {
- for (int j = 0; j < 500; j++) {
- volatoleAtomicityDemo.increase();
- }
- });
- }
- // 等待1.5秒,保证上⾯程序执⾏完成
- Thread.sleep(1500);
- System.out.println(inc);
- threadPool.shutdown();
- }
- }