并发编程三大原则之初识synchronized-CSDN博客
在上一篇文章中,我们已经知道了,synchronized关键字的基本使用了。那么我们是不是需要了解一下,这个关键字到底在程序中是如何使用的,是怎么保证并发编程的,三个原则的。
首先,我们看一下在我们的java程序被编译成 class 文件后,这个关键字在 class 文件中,是已什么样的方式展示出来的。
这个是.java文件中的定义。
这个是 字节码中,synchronized 所展示出来的样子,就是把 synchronized 所包含的代码块,使用了两个 指令 给包起来了。
MONITORENTER 进入监视器。
MONITOREXIT 退出监视器。
以上是从 java 文件到字节码的展示结果。
我对于监视器的两个关键字的理解就是,在程序中,添加了提示装置,当程序读到 MONITORENTER 这个指令的时候,那么 jvm 就会知道我即将运行的是一段被 synchronized 修饰的代码段。
MONITOR 在jvm所对应的就是ObjectMonitor 对象,这个对象里面包含了很多信息。
拿一个比较重要的信息举例:
owner:一开始这个是 null 一旦有线程操作,同步代码块,那么这个owner就会被赋值成为当前对象。在owner没有被赋值为 null 的时候,其他线程是无法操作同步代码块的内容。(这个在我看来的话就是保证了线程执行代码的原子性,就是你关上门,自己吃苹果,你没有吃完,其他人进不去)
于是乎,jvm 就开始对该对象的 markworld 进行分析,得出当前锁的一个状态,得出来不同的状态值后呢,还会得出当前对象是个什么级别的锁。然后 jvm 在调用C++代码中,不同的 case 块,来执行不同的逻辑,举个例子:
在 C++ 层面执行代码的时候,会把 class 翻译成汇编码,丢给操作系统去完成,需要做的事情。
这里面还有一个事情就是在 执行 synchronized 代码块的时候,汇编指令前面会加上一个 lock 指令,在 cpu 看到有 lock 指令的时候,就会触发 总线嗅探机制,这个机制的目的是为了保证数据的一致性。当多线程在操作共享变量的时候,一个线程操作完,会立马通过总线刷新到主存中,在经过总线的时候,总线就会通知其他使用到,该变量的线程内部的副本变量,就会失效,用的时候必须要从主存中重新加载才行。
这里面的总线,你简单的理解成,CPU 和 主存 之间连线。
通过 加上 lock 指令,就可以保证并发编程的,数据的可见性。
那么有序性是怎么保证的呢,这个在 synchronized 并没有很好的体现出来,因为这个关键字保证不了有序性,后面会通过 DCL 的实现方式来验证该问题。也会给出保证有序性的解决方案。