synchronized使用及锁升级过程

86 阅读2分钟

synchronized锁

几种使用方式

  1. 直接往方法上加synchronized关键字:

        //方法上加锁,锁的是当前对象。
        //如果在静态方法上加锁,锁的是类,也就是class对象
        public synchronized void temp(){
    ​
        }
    
  2. 同步代码块:

        //可以使用对象也可以使用类的.class对象作为监视器
        synchronized (MyTread.class){
                System.out.println("hello");
        }
    

锁升级

对象头

synchronized加锁其实就是给一个对象加锁,锁存在对象头中,不同线程争抢一个对象的对象头中的锁。

对象头结构:

1706931911884.png

而锁信息又存放在对象头中的mark word中。

mark word结构:

1706932027738.png

锁升级步骤

无锁========>偏向锁========>轻量级锁========>重量级锁

锁升级详细过程

偏向锁:

  1. 判断对象头中Mark Word的Thread Id是否为空
  2. 若为空cas设置Thread Id为当前线程
  3. 若不为空,检查Thread Id是否为当前线程Id
  4. 如果是当前线程,拿到锁直接执行同步代码块
  5. 如果失败则检查偏向锁的标识是否为1,如果不是CAS竞争锁
  6. 如果有偏向锁则用CAS尝试替换Mark Word中的线程Id,进行偏向锁撤销

1706274009106.png

偏向锁撤销:

  1. 需要等待到全局安全点(在这个时间点上没有执行的字节码文件)
  2. 会短暂暂停拥有偏向锁的线程,判断持有锁的线程是否存活,如果线程不在活动状态,则将对象头设置为无锁状态
  3. 如果持有锁的线程存活,对象头锁标记位设置为轻量级锁,唤醒持有锁的线程

轻量级锁加锁:

  1. 在线程执行同步代码块之前,JVM会在当前线程的栈帧中创建一个储存锁记录的空间lock record,将对象头中的Mark Word复制到lock record中,官方称为Displaced Mark Word。
  2. 当前线程会尝试使用CAS将Mark Word中的指针指向lock record,如果成功则获得锁,如果失败表示有其他线程竞争,会通过自旋来获得锁

1706274063261.png

轻量级锁解锁:

轻量级锁解锁时会将lock record替换回对象头,如果成功则表示没有竞争发生,如果失败则轻量级锁会膨胀为重量级锁。

由于自旋锁长时间没有获取到锁的话会消耗cpu资源,为了避免无用的自选,锁一旦升级为重量级锁之后,不会恢复到轻量级锁。

进入重量级锁之后,一旦第一个线程获得锁之后,其他线程尝试获得锁就会进入阻塞状态,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程会重新竞争锁。