volatile关键字修饰共享变量想必已经不陌生了,它的一直是面试中的热点,为何它具有如此高的热度呢?
我的理解:我们在实现并发问题的时候一律采用锁机制,甚至只使用内部锁,没问题,程序一般不会出现大的问题。但是,性能往往是我们追求的,有时候我们甚至愿意牺牲一些正确性来保证性能。那么,volatile为什么叫做轻量级同步机制,就是在某些情况下我们可以灵活使用volatile来提高性能,这对于一个人的能力是很好的考察。好吧,咱们先来了解一下它吧。
volatile:变量的读写操作都必须从高速缓存或主存中读取,以读取变量的相对新值。volatile变量不会被编译器分配到寄存器存储,对volatile变量的读写操作都是内存访问。
1.保证可见性:这应该是volatile最直观最易于理解的作用。原因很简单,因为每次读写操作都是从高速缓存或内存中取值,那么这个值就是之前所有线程对这个变量修改的结果。其保证机制是通过内存屏障实现的。
2.保证有序性:其实也是通过内存屏障实现的。它相当于告诉jit编译器不要做代码优化,从而避免有序性问题。
对volatile变量的写操作:
对volatile变量的读操作:
3.保证读写操作本身的有序性:(注意一下我刚开始看书有点迷糊)
之前我们说到过long/double变量的操作的原子性问题,建议采用volatile来修饰以保证它的原子性。
所以,千万不要理解成volatile可以保证原子性。
另一方面,它是保证写操作本身的原子性,不保证赋值操作的原子性。例如:i = i+1;也就是等号右边不能含有共享变量。
这里有一个很重要的点,对于 数组 和 对象:
volatile同样只保证读写操作本身,即保证 引用(指针或者内存地址) 是相对新值,而不能保证数组里的元素或者对象里面的字段是相对新值。
volatile的开销介于锁和普通读写操作之间,它会刷新和冲刷缓存,但是不会引起上下文切换。
那么,我们来看一下volatile的应用场景:
1.使用volatile来作为变量的状态标志。这其实就是在多线程之间起个通信的作用。
2.使用volatile代替锁(volatile并非锁的替代品)。例如之前我们的场景,多线程共享一组变量,那么我们可以把这一组变量封装成一个对象,每次修改它时,我们新建一个对象,并把这个对象赋给原来对象的引用。 我想对于更新操作频繁的应用并不适合,因为新建对象也是有开销的。
3.用volatile实现简易读写锁。简单来说就是volatile与锁混用。对于共享变量仅仅是对写操作加锁。所以当一个线程在读取变量的值时,另外的线程可以修改。那么,这个系统就是允许读取到的是变量的非最新值。
好了,volatile就为大家写到这了。因为最近要面对找实习的问题,所以吧感受很多,作为一个貌似过来人有很多话想说,准备也来写一篇情感文章,分享一下末流985学生的真实情况。