volaatile关键字是java中同步机制中一个非常重要的关键字,它保证了可视性。如果一个域被声明为volatile,那么只要对这个域产生写操作,那么所有的读操作都可以看到这个修改。这就是同步,在并发中是非常重要的。如果资源被某个任务修改,但是其他读取这个资源的任务并不知道,那么是会发生问题的,就像你老婆怀孕了,但是在你知道这个消息前,你并不知道她和其他男人上过,那么你得到这个怀孕的消息时,就会发生严重的问题,孩子可能不是你的,但是你并不知道啊!撇开这个我胡乱编造的例子,大家应该可以知道同步的重要性了吧,尤其是男人。
并不是所有对某个域有写操作就要声明为volatile,像是一个任务的任何写操作对于这个任务都是可视的,所以,如果这个资源只是在这个任务内部工作,是大可不必声明为volatile的。而且也并不是所有的域有资格声明为volatile,如果一个域的值依赖于它之前的值时,就像递增,volatile是发挥不了作用,还有,如果某个值受到其他域的限制,也是不能用的。为什么不能用呢?这也是一个问题。就一条一条理清楚好了。一个域的值依赖于它之前的值,就是说,这个值是时刻变化的,而且它的变化是基于它前面的值的,volatile是可视性的问题,拿递增来说好了,如果这个域是可视的,那么就是所有读的任务都可以知道这个域,又因为这些操作都是非原子性的,所以是可以被中断的,那么,如果一个任务在递增的过程中就将它中断并获得这个操作的中间值,那么就会发生错误,因为我们想要的,是最后的结果,而不是不稳定的中间值。接下来是某个值受到其他域的限制,打个比方,假设一个值i,它的使用条件是i < j,j是另一个域的值,那么为什么不能用呢?很简单,你这个域是可视的,但另一个域是不可视的,但是这个域的使用是要知道另一个域的,这当然会存在问题,将另一个域也设为可视?是啊,这就可以解决了,但是这多麻烦,多不实际,程序的设计多难堪啊!
什么情况下使用volatile是绝对安全的?就是类中只有一个可变的域。
volatile的使用总是有点不安全,所以一般情况下,都是使用synchronized,这是最安全的方式,那么会有非得使用volatile的情况吗?其实这种问题的答案,一般来说,如果是同步一个方法,使用synchronized,如果是同步一个数据,使用volatile.
对于volatile,有一句让初学者晕头转向的话:”使用volatile能够确保编译器不进行任何优化动作“。为什么不让编译器进行优化动作啊?那么我们就要知道,编译器对于读写的优化动作是怎样的。目前知道的是,编译器的优化动作是用线程中的局部变量来维护对这个域的精确同步。这里可以理解,就是说当这个域发生改变的时候,会在线程中创建一个局部变量来保存这个改变的值,并赋值给这个线程的域,然后将这个读写的操作移除。那么,就是volatile为什么要防止这种行为?这种行为对于可视性到底有什么影响?当然有,读写操作被移除了,那么其他任务怎么知道啊!