volatile
volatile可以保证修饰变量的可见性和有序性,对于被volatile修饰的变量,对其进行单个读写,等价于被synchronized修饰的读操作或者写操作。
如何保证可见性
实现情况视处理器而定,在intel处理器上,对被volatile修饰的变量进行写操作的指令,在翻译为汇编指令时,会加上lock前缀,处理器在执行这一指令时,会将缓存行中的数据写会内存,其他CPU会通过嗅探总线,如果本地内存内缓存了此变量,会时当前值无效,重新读取。
如何保证有序性
在对volatile进行读写操作指令在编译为字节码时,会通过在指令序列中插入内存屏障指令来预防编译器和处理器为提高执行效率而进行的指令重排序,以此保证执行的有序性。
synchronized
java中的每一个对象都可以作为锁,使用synchronized加锁,根据使用场景,有三种不同的情况:
- 对于普通同步方法,锁是实例对象;
- 对于同步代码块,锁是当前类的Class对象(访问当前类方法和属性的入口);
- 对于同步方法块,锁是括号中配置的对象。
synchronized用的锁是存在Java对象头里的,Java对象头中的Mark Word默认存储对象的hashCode,分段年龄和锁标记位。
fianl域的内存语义
编译器和处理器对于final域的的处理,在进行重排序时需要遵守两个规则:
- 在构造函数内对一个final域的写入与随后将这个引用赋值给其他引用的操作不能重排;
- 初次读一个包含final域对象的引用,与随后读这个final域,不能重排。
实现以上规则同样是依赖在字节码指令序列中插入内存屏障。