一:volatile
关于volatile的功能无非是两个: 1:保证保证线程间变量的可见性 2:防止指令重排序 下面我们就从字节码层面,JVM层面,CPU层面来解析是如何做到的。
1:字节码层面
首先编写一个很简单的类:
public class TestVolatile { int i; volatile int j; }
编译并查看字节码: 由此可知:在字节码层面,加上volatile会给变量加上ACC_VOLATILE访问标识
2:JVM层面
这部分,无法用代码验证了,理论分析,其实是加了JVM层面的内存屏障,注意,是JVM规范的内存屏障,并不是真正的内存屏障。 JVM规范了四种内存屏障
而volatile则是在内存区的读写,都加屏障,具体如下,建议背过: StoreStoreBarrier:volatile写操作未执行前,保证前面的写操作完成 volatile 写操作 StoreLoadBarrier:volatile写操作执行完成前,后续不进行读写操作 LoadLoadBarrier:volatile 读操作未执行前,保证前面的读操作完成 volatile 读操作 LoadStoreBarrier:volatile 读操作执行完成前,后续不进行写读操作
3:CPU层面层面
上面说了JVM层面的内存屏障,其实说到底,JVM只是在内存中运行的一个软件而已,真正保障volatile功能还是依靠底层的硬件。 其实,cpu也有内存屏障,但是只有三条。 写屏障 sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。 读屏障 lfence:load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。 读写屏障 mfence:modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。 还有条汇编指令Lock 原子指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。 Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序 总结:其实,volatile是windows系统使用lock 指令实现 | MESI实现(MESI:缓存一致性协议,基于inter类型的CPU)
二:synchronized
1:字节码层面
同样的,synchronized的作用是实现同步操作我们主要来分析它的实现原理。 首先,创建一个类:
public class TestSync { //加在方法m上 synchronized void m(){} //加在方法n上的代码块上 void n() { synchronized (this) { } } public static void main(String[] args) { } }
查看字节码:
从字节码可以看出,在方法上和代码块上加synchronized在字节码层面的实现是不一样的: 加在方法上:多了一个ACC_SYNCHRONIZED的访问标志 加在代码块上:1个monitorenter 两个monitorexit —>告诉虚拟机需要加锁
注意:这里为什么要两个monitorexit?
答:众所周知,synchronized在执行完成后会释放锁,monitorenter是加锁的开始,第一个monitorexit是正常执行完成后跳出释放锁的,第二个monitorexit是执行过程中如果抛出异常,是synchhronized自动退出的,防止继续持有锁,防止死锁的产生。
2:JVM层面
无法用代码实现,但我们都知道,Java是基于C/C++实现的,所以是C /C++ 调用了操作系统提供的同步机制
3:CPU层面
也是调用了CPU的lock指令 ,X86 : lock cmpxchg / xxx
本站声明:
1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/293023.html