java:对象和锁

java是面向对象的编程语言,主要针对的就是对象。

近期深入学习了一下相关知识,作为知识的搬运工,把总结的一些知识记录如下。

在java中,对象的结构宏观上可以看作为以下结构:


对象头(Header)主要分为两部分构成:

1、Mark World:主要存储哈希码、GC分代年龄、锁的标志位等信息。以32bit为例,其中4bit用于存储GC分代,1bit用于表示是否偏向(0未偏向 1偏向),2bit用于锁的标志位,剩余25bit 主要 用于存储哈希码和持有线程等。

2、类型指针:主要用于表示对象实例属于哪个类。

实例数据:

主要用于存储对象的实际数据。

对其填充:

本质上,主要起占位符的作用。因为根据JVM的要求,对象分配的空间大小必须是8的整数倍。

后两部分还是比较好理解的,现在主要说说有关对象头的一些使用场景,这就结合了后面我们说的锁。

JDK在1.6的版本以后,synchorized关键字的具体细节大概有偏向锁,轻量级锁以及重量锁等。这里主要说下偏向锁和轻量级锁。

偏向锁

        本质上是在对象头的Mark World区域设置一个ThreadId,用来表明使用线程,主要是为了减少加锁解锁以及过多的CAS操作。(因为很多时候,并不存在多线程的竞争)

        大概的流程如下:首先对象Mark World内有一个ThreadId,初始的时候为0,偏向标志位也为0,对象处于biasable状态,锁的标志位为01,表明锁可获得。当有一个线程尝试去操作这个对象,首先使用CAS去尝试将ThreadId改变。如果失败,说明此时存在竞争,那么会在一个相对安全的点将获得锁的线程挂起,将锁升级为轻量级锁。如果成功的话,偏向标志位也为1,对象处于biased状态,进而执行同步代码块,然后释放锁。

轻量级锁

        在进入代码块时,如果对象未锁定(锁标志位为01状态),那么将在虚拟机线程的栈帧中创建一个名为Lock Record的空间(好像也有叫Displaced Mark Word),用于存储当前对象Mark Word的拷贝。然后虚拟机尝试使用CAS将对象的Mark Word替换为Lock Record的引用,如果该操作成功了,标志位变为00,即此时对象获得了轻量级锁。如果失败,则会自旋,进而变为重量级锁,标志位为10。随后进行解锁,如果对象的Mark Word仍然指向线程的Lock Record,那么用 CAS 操作把对象当前的 Mark Word 和线程中复制的 Displaced Mark Word 替换回来。

        轻量级锁能提升程序同步性能的依据是 “对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量的开销外,还额外发生了 CAS 操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。

你可能感兴趣的:(java:对象和锁)