volatile关键字小记
这两天在看FSEditLog类的时候遇到了volatile关键字和ThreadLocal变量,对这部分的知识已经有点模糊,故记此一篇已备忘,本篇介绍volatile,有关ThreadLocal的之后再整理。
其实java并发的问题根因是两个,一是 高速缓存,二是 乱序执行。下面简单介绍一下这两个根因。
高速缓存其实描述的是物理机内存的模型,为了提高速度,每个处理器都对应一个高速缓存,而所有的高速缓存都共享一个主内存区域,因此主内存和高速缓存之间就有一个一致性的问题。对应到java内存模型中,每个java线程都对应一个工作内存,而所有工作内存都共享一个主内存。
乱序执行对应的场景是指令重排序优化,也就是指处理器采用了允许将多条指令不按程序规定的顺序分开发送给各个相应的电路单元进行处理,当然这不是对指令的任意重排,因为处理器会保证正确处理指令依赖情况以得到正确结果。
volatile在这两个根因上都做了工作,首先在高速缓存上,被volatile修饰的变量被编译成指令后,该指令后都会跟一个lock指令,lock指令的作用是将本处理器的缓存写入内存,同时无效化其它处理器的缓存。因此volatile的第一个作用是使 修改对所有线程都可见。
其次在乱序执行上,同样也是lock指令起了作用,因为lock指令把修改同步到内存中的时候,意味着所有之前的操作都已经执行完成了。(注:这里请注意一点,根据程序反编译出的汇编指令顺序其实和程序顺序是一致的,只是处理器在执行时会对指令重排序优化)
虽然volatile在两个根因上都做了工作,但它不能保证线程安全,比如对volatile修饰的int变量进行多线程自增,因为自增不是原子操作,所以并不能保证并发正确。