volatile和synchronized的区别
- 区别
- 实现原理
区别
总结以下几条:
1、从功能上,volatile是告诉jvm当前变量在寄存器中的值是不对的,需要从主存中读取;synchronized是锁定当前变量或者代码,只有当前线程可以访问,其他线程被阻塞。
2、volatile只能修饰变量。synchronized可以修饰类、方法、静态方法、方法块。其中其修饰类、静态方法时作用的这个类的所有对象。如果子类重写父类的synchronized的方法且不加synchronized修饰,子类不会继承父类的synchronized关键字,即子类的方法默认不同步。
3、volatile是轻量级的;synchronized是重量级的。
4、volatile只保证有序性和可见性;synchronized保证原子性、有序性和可见性。
5、volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
实现原理
volatile如何实现可见性和有序性?
1、可见性
对volatile修饰的变量进行赋值修改的时候,JIT编辑器所生成汇编代码会在最后有一行:
0x01a3de24:lock addl $0x0,(%esp);
代码是什么不重要,这行代码的作用是对原值加0,是一个空操作,lock前缀指令的作用是:将当前处理器缓存行的数据写回系统内存;这个写操作会使在其他CPU或者别的内核缓存了该内存地址的数据无效。而缓存一致性协议,当其他线程操作该变量时发现其无效了,就会去系统内存去重新读取数据,这就保证了被volatile修饰的变量对所有线程可见。
2、有序性
这是因为volatile变量在执行赋值操作后会产生一个内存屏障,在执行到内存屏障时,其前面的所有操作都已经完成,其后面的操作不会早于内存屏障执行。
举个例子:
// 线程A:
a = 1; // volatile a = 1;
b = 2;
// 线程B
while(true){
if(b == 2){
c = a;
...
}
}
变量a未被volatile修饰时,由于重排序,线程A可能先执行b=2,此时线程B运行,满足进入判断条件,对c进行了赋值,但却不是想要的1,而如果a被volatile修饰,则不会出现这个问题。
synchronized实现原理
sychronized实现互斥同步的原理。sychronized关键字进行编译后,会在同步块的前后形成monitorenter和monitorexit两个字节码命令。在执行monitorenter命令时,首先尝试获取sychronized指定对象的锁,如果该对象未被锁定,则当前线程获取该对象的锁,并将锁的计数器加1,相应的,执行monitorexit命令时,锁的计数器减1,减到0的时候,释放该对象的锁。如果获取该对象的锁失败,则会进入阻塞状态,直到对象锁被另一个线程释放。当然,对于同一线程,synchronized同步块时可重入的,不会出现自己把自己锁死的情况,重入时,锁计数器加1。