volatile只能修饰类变量和实例变量,对于方法参数,局部变量,实例变量,类常量都不能修饰
我们的主内存(RAM)(一共有三级缓存,L3 cache速度最快,依次降低)的读写速度远远落后于CPU cache,Cache的出现是为了解决cpu直接访问内存效率低下问题的,程序在运行过程中,会将运算所需要的数据从主存复制一份到CPU cache中,这样CPU进行计算时就可以直接对CPU cache中的数据进行读取和写入,当运算结束之后,再将CPU cache中的最新数据刷新到主内存中,CPU通过直接访问cache的方式替代直接访问主存的方式极大提高了CPU的吞吐能力
- 共享变量存储于主内存中,每个线程都可以访问
- 每个线程都有私有的工作内存或者成为本地内存
- 工作内存只存储该线程对共享变量的副本
- 线程不能直接操作主内存,只有先操作了工作内存之后才能写入主内存
假设主内存的共享变量为0,线程1和线程2分别拥有共享变量x的副本,假设线程1此时将工作内存中的x修改为1,同时刷新到主内存中,当线程2想要去使用副本x的时候,就会发现该变量已经失效了,必须到主内存中再次获取然后存入自己的工作内存中。
并发编程有三个重要特性
- 原子性
所谓原子性是指在一次的操作或者多次的操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。i++不能保证原子性,volatile关键字不保证数据的原子性,synchronized关键字保证
多个原子性操作合在一起就不是原子性操作了
简单的读取和赋值操作是原子性的,将一个变量赋值给另外一个变量的操作不是原子性的
由于synchronized是一种排他机制,因此被他修饰的同步代码是无法被中途打断的,因此其能够保证代码的原子性。 - 可见性
可见性是指当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的最新值
synchronized和Lock保证可见性,他们会在(monitor exit)锁释放之前,会将对变量(共享资源)的修改刷新到主内存中
volatile具有保证可见性的语义 - 有序性
有序性是指程序代码在执行过程中的先后顺序,由于Java在编译器以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序
单利模式中的double-check的写法就是利用了volatile的顺序性
volatile保证了不同线程对共享变量操作时的可见性,也就是说当一个线程修改了volatile修饰的变量,另外一个线程会立即看到最新的值
volatile禁止JVM和处理器对使用volatile修饰的关键字进行指令进行重排序
i++的操作其实是由三步组成:
- 从主内存中获取i的值,然后缓存到线程的工作内存中
- 在线程工作内存中为i进行加1的操作
- 将i的最新值写入主内存中
网友评论