其他分享
首页 > 其他分享> > synchronized实现原理

synchronized实现原理

作者:互联网

synchronized实现原理

1. Java对象头

在 JVM 中,不同变量类型的对象头大小是不同的。

在 32 位虚拟机中,1 字宽等于 4 字节,即 32bit。在 64 位虚拟机中,1 字宽等于 8 字节,即 64 bit。

Java 对象头由三部分组成:

长度 内容 说明
32/64bit Mark Word 存储对象的 HashCode 或锁信息
32/64bit Class Metadata Address 指向存储对象类型数据的指针
32/64bit Array length 数组长度(如果当前对象是数组)

Mark Word 中存储的数据会根据锁标志位的变化而变化,如下表所示:

64 位虚拟机中共 64bit:

image-20220131133503194

2. synchronized实现原理

synchronized 实现同步的基础是 Java 中每个对象都可以作为锁,具体表现为以下三种形式:

当线程试图访问同步代码块时,必须先获得锁,退出或抛出异常时必须释放锁。

synchronized 同步语句块的基础是 monitorenter 指令和 monitorexit 指令。monitorenter 在编译后插入同步代码块的开始位置, monitorexit 插入方法结束处和异常处。JVM保证了每个 monitorenter 都必须有 monitorexit 指令与之配对。线程执行到 monitorenter 指令时,将尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁

synchronized 同步方法的原理是在字节码中为方法添加一个ACC_SYNCHRONIZED标识,指明该方法是一个同步方法。当看到这个标识后,自动在方法调用和退出时加上monitorenter 指令和 monitorexit 指令。

3. synchronized锁升级

在 Java1.6 中,为了减少锁的获得和释放带来的性能开销,引入了偏向锁轻量级锁。于是锁一共有四种状态,由低到高依次是:

这几个状态会随着竞争情况逐渐升级,锁可以升级但不能降级,提高了获得锁和释放锁的效率。

3.1 偏向锁

由于大多数情况下,锁不仅不存在多线程竞争,而且总是有一个线程多次获得。为了让线程获得锁的代价更低,Java 虚拟机引入了偏向锁

获得偏向锁

撤销偏向锁

偏向锁的撤销,需要等待一个全局安全点,即这个时间点上没有正在执行的字节码。

image-20220131140009312

禁用偏向锁

偏向锁时默认启用的,但是在应用程序启动几秒后在延迟激活。可以使用-XX:BiasedLockingStartupDelay=0参数来关闭延迟。如果确定应用程序所有的锁通常情况下处于竞争状态,可以通过-XX:UseBiasedLocking=false来禁用偏向锁。那么程序默认会进入轻量级锁状态。

3.2 轻量级锁

轻量级锁加锁

轻量级锁解锁

image-20220131141846884

3.3 重量级锁

重量级锁就是利用对应的对象作为锁,获取不到就阻塞。

任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或同步方法。没有获取到监视器的线程会被阻塞在入口处,进入 BLOCKED 状态。

image-20220131142941675

当获得锁的线程释放了锁,还会唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。

4. 锁的对比

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外消耗,和执行非同步方法相比仅存在纳秒级的差距。 如果线程间存在锁竞争,会带来额外的锁撤销的消耗。 适用于只有一个线程访问同步块的场景。
轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度。 如果始终得不到锁竞争的线程,使用自旋会消耗 CPU。 追求响应时间且同步块执行速度非常快。
重量级锁 线程竞争不用自旋,不消耗 CPU 线程阻塞,响应时间缓慢。 追求吞吐量且同步块执行时间较长。

标签:同步,Word,synchronized,实现,Mark,对象,线程,原理,偏向
来源: https://www.cnblogs.com/WangXianSCU/p/15858272.html