synchronized的实现原理-java并发编程的艺术读书笔记

时间:2023-12-12 08:47:14

1.synchronized实现同步的基础

Java中的每个对象都是可以作为锁,具体有3种表现。

1.对于普通同步方法,锁是当前实例对象。

2.对于静态同步方法,锁是当前类的Class对象。

3.对于同步方法块,锁是Synchonized括号里面的配置对象。

当前一个线程试图访问同步代码块时,它首先必须得到锁,退出或者抛出异常时候必须释放锁。那么锁到底存在什么地方?

从JVM规范可以看到Synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码快同步,但两者的实现细节不一样。synchronized关键字编译后会在同步块的前后添加上montorenter和monitorexit两个字节码指令,这两个字节码指令都需要一个指向锁定和解锁对象的reference,如果指定了同步的对象reference就指向这个对象,如果修饰的是方法,如果是类方法就指向Class对象,如果是实例方法就指向这个实例。

2.Java对象头

锁存在Java对象头里。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,一字宽等于四字节,即32bit。

Java对象头里的Mark Word里默认存储对象的HashCode,分代年龄和锁标记位。

在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据:轻量级锁、重量级锁、GC标记、偏向锁

3.锁的升级

Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

4.偏向锁

Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”概念,Java SE1.6一共有4种状态,无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争情况不断升级,锁可以升级但是不能降级。

锁对象第一次被线程获取的时候,虚拟机把对象头的标识位设置为"01",可偏向模式,如果通过CAS操作把mark word设置为当前线程的ID,如果CAS操作成功,这样以后这个线程每次进入这个锁的同步块时,虚拟机不再进行任何操作。 一旦有其他线程竞争这个锁,则偏向模式宣布结束。

5.轻量级锁

轻量级锁加锁:线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

轻量级锁解锁:轻量级解锁时,会使用原子的CAS操作来将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。