说一下volatile吧
volatile的作用是什么?
1)保证变量对所有线程的可见性。当一条线程修改了变量值,新值对于其他线程来说是立即可以得知的。
2)禁止指令重排序优化。使用 volatile 变量进行写操作,汇编指令带有 lock 前缀,相当于一个内存屏障,编译器不会将后面的指令重排到内存屏障之前。volatile的特性有哪些?
并发编程的三大特性为可见性、有序性和原子性。通常来讲volatile可以保证可见性和有序性。
1)可见性:volatile可以保证不同线程对共享变量进行操作时的可见性。即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。
2)有序性:volatile会通过禁止指令重排序进而保证有序性。
3)原子性:对于单个的volatile修饰的变量的读写是可以保证原子性的,但对于i++这种复合操作并不能保证原子性。这句话的意思基本上就是说volatile不具备原子性了。voliatile的实现原理?
前面已经讲述volatile具备可见性和有序性两大特性,所以volatile的实现原理也是围绕如何实现可见性和有序性展开的。volatile实现内存可见性原理
导致内存不可见的主要原因就是Java内存模型中的本地内存和主内存之间的值不一致所导致,例如上面所说线程A访问自己本地内存A的X值时,但此时主内存的X值已经被线程B所修改,所以线程A所访问到的值是一个脏数据。那如何解决这种问题呢?
volatile可以保证内存可见性的关键是volatile的读/写实现了缓存一致性,缓存一致性的主要内容为:
每个处理器会通过嗅探总线上的数据来查看自己的数据是否过期,一旦处理器发现自己缓存对应的内存地址被修改,就会将当前处理器的缓存设为无效状态。此时,如果处理器需要获取这个数据需重新从主内存将其读取到本地内存。
当处理器写数据时,如果发现操作的是共享变量,会通知其他处理器将该变量的缓存设为无效状态。
那缓存一致性是如何实现的呢?可以发现通过volatile修饰的变量,生成汇编指令时会比普通的变量多出一个Lock指令,这个Lock指令就是volatile关键字可以保证内存可见性的关键,它主要有两个作用:
将当前处理器缓存的数据刷新到主内存。
刷新到主内存时会使得其他处理器缓存的该内存地址的数据无效。volatile实现有序性原理
前面提到重排序可以提高代码的执行效率,但在多线程程序中可以导致程序的运行结果不正确,那volatile是如何解决这一问题的呢?
为了实现volatile的内存语义,编译器在生成字节码时会通过插入内存屏障来禁止指令重排序。
内存屏障:内存屏障是一种CPU指令,它的作用是对该指令前和指令后的一些操作产生一定的约束,保证一些操作按顺序执行。volatile能使一个非原子操作变成一个原子操作吗?
volatile只能保证可见性和有序性,但可以保证64位的long型和double型变量的原子性。
对于32位的虚拟机来说,每次原子读写都是32位的,会将long和double型变量拆分成两个32位的操作来执行,这样long和double型变量的读写就不能保证原子性了,而通过volatile修饰的long和double型变量则可以保证其原子性。volatile、synchronized的区别?
1)volatile主要是保证内存的可见性,即变量在寄存器中的内存是不确定的,需要从主存中读取。synchronized主要是解决多个线程访问资源的同步性。
2)volatile作用于变量,synchronized作用于代码块或者方法。
3)volatile仅可以保证数据的可见性,不能保证数据的原子性。synchronized可以保证数据的可见性和原子性。
4)volatile不会造成线程的阻塞,synchronized会造成线程的阻塞