Java锁-Synchronized深层剖析 (6)

上面的解释非常简单,或者说粗糙,实际的判定方式更为复杂。我在查阅资料时,发现网上很多博客根本没有深入说明偏向锁升级轻量级锁的深层逻辑,直到看到一篇博客写出了以下的说明:

当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

这段说明的前半截,我已经在偏向锁部分说过了。我来说明一下其后半截有关锁升级的部分。

如果当前线程(锁的竞争者线程)的线程ID与锁对象的mark word的thread不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活(可以直接根据锁对象的Mark Word(更准确说是Displaced Mark Word)的thread来判断线程1是否还存活),如果没有存活,那么锁对象被重置为无锁状态,从而其它线程(线程2)可以竞争该锁,并将其设置为偏向锁(等于无锁状态下,重新偏向锁的竞争);如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果线程1还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。(这个地方其实是比较复杂的,如果有不清楚的,可以@我。)

那么另一个由无锁状态升级为轻量级锁的内存过程,就是:

首先让我来说明一下上面提到的“如果线程1还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁”涉及的三个问题。

为什么需要暂停线程1

如何撤销偏向锁

如何升级轻量级锁

第一个问题,如果不暂停线程1,即线程1的虚拟机栈还在运行,那么就有可能影响到相关的Lock Record,从而导致异常发生。

第二个问题与第三个问题其实是一个问题,就是通过修改Mark Word的锁标志位(lock)与偏向锁标志(biased_lock)。将Mark Word修改为下面形式:

含义 thread epoll age biased_lock lock
示例   aaa...(23位bit)   bb(2位bit)   xxxx(4位bit)   1(1位bit ,具体值:1)   01(2位bit ,具体值:01)  

在代码进入同步块的时候,如果锁对象的mark word状态为无锁状态,JVM首先将在当前线程的栈帧)中建立一个名为锁记录(Lock Record)的空间,用于存储Displaced Mark Word(即锁对象目前的Mark Word的拷贝)。

有资料称:Displaced Mark Word并不等于Mark Word的拷贝,而是Mark Word的前30bit(32位系统),即Hashcode+age+biased_lock,不包含lock位。但是目前我只从网易微专业课听到这点,而其它我看到的任何博客都没有提到这点。所以如果有谁有确切资料,希望告知我。谢谢。

锁的竞争者尝试获取锁时,会先拷贝锁对象的对象头中的Mark Word复制到Lock Record,作为Displaced Mark Word。然后就是之前加锁过程中提到到的,JVM会通过CAS操作将锁对象的Mark Word更新为指向Lock Record的指针(这与之前提到的修改thread的CAS操作毫无关系,就是修改锁对象的引用变量Mark Word的指向,直接指向锁的竞争者线程的Lock Record的Displaced Mark Word)。CAS成功后,将Lock Record中的owner指针指向锁对象的Mark Word。而这就表示锁的竞争者尝试获得锁成功,成为锁的持有者。

而这之后,就是修改锁的持有者线程的Lock Record的Displaced Mark Word。将Displaced Mark Word的前25bit(原identity_hashcode字段)修改为当前线程(锁的竞争者线程)的线程ID(即Mark word的偏向锁结构中的thread)与当前epoll时间戳(即获得偏向锁的epoll时间戳),修改偏向锁标志位(从0变为1)。

听得有点晕晕乎乎,来,给你展示之前那位大佬的流程解释(另外我还增加了一些注释):

轻量级锁的加锁过程(无锁升级偏向锁):

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdpwz.html