【JDK源码】Synchronized关键字原理,和锁的膨胀过程

Synchronized关键字原理,和锁的膨胀过程

一:Synchronized关键字介绍

synchronized是Java中的关键字,用于线程的同步。可以用在三个地方。

​ 1:同步实例方法,锁是当前实例对象;

​ 2:同步类方法,锁是当前类对象;

​ 3:同步代码块,锁是括号里面的对象;

二:Synchronized的原理解析

1:synchronized是JVM内置锁,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现。

我们对Synchronized的代码进行编译成字节码文件后,可以看到

public class SynchronizedDemo {
  
  private final Object object = new Object();
  @Test
  public void test01() {
    synchronized (object) {
      System.out.println("true = " + true);
    }
  }
}

比如这段代码,我们锁的object对象,但是jvm怎么实现锁定对象的呢?

首先我们认识一下对象在jvm里面内存结构。

JVM锁的膨胀过程

内存中java对象是通过mark word在记录锁的状态。分表记录了轻量级锁,重量级锁,偏向锁,无锁的四种状态。

通过锁的标记位标记锁的状态。

比如现在线程T1进入获取锁的代码,首先判断这个锁对象(如:标记的object对象)首先检查锁的标志位,如果是01,然后再判断是否未偏向锁,检查标记是否未偏向锁的bit位是否位1,如果是1的话当前对象处于就是偏向锁,如果是0的话就是无锁状态,无锁的话,通过CAS修改锁的状态位偏向锁。

这个时候线程T2来获取锁,首先检查当前锁的状态,此时处于偏向锁状态。然后判断当前占用锁的线程ID是否位为自己。明显当前偏向锁的占有者是t1线程

这时候,t2线程会尝试 通过CAS修改,这个CAS循环获取锁的此时jvm可以有限制的,再指定次数获取失败后。

就说检查当前占用锁的t1线程是否走完同步代码块,如果走完了,设置当前占用锁的线程id为null。这个时候线程t2就可以获取到锁了。

如果t1没有走完同步代码块,还在进行同步逻辑。这个时候偏向锁就会升级为偏向锁,将锁的标记为设置为00(前面图中所示)。

这个时候锁的标记位如下图。

这个时候我们的t1线程,在线程栈占用开辟一块空间,执行我们的锁对象,同时锁对象中的Mark word中也会有空间指针指向,t1线程栈中的空间。如下图所示。

这个时候t2同样没有获取到锁,这时候t2同样通过CAS(一定次数)修改mark word中轻量级锁的指针。假如继续获取失败。

锁就会继续膨胀升级位重量级锁。这个时候锁的Mark word 如下图

这个时候,锁就进入了java6以前版本的模式了。t2就会被阻塞挂起来,等t1执行结束后重新被唤醒。

synchronized是JVM内置锁,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁》实现

总结

synchronized在java6 以后做了升级,性能不输我们java.util.concurrent包中的lock锁。同时我们看Synchronized原理知道,我们在用Synchronized关键字同步代码块的时候,锁定的范围尽量小,执行的时间尽量控制短一些。不然锁容易膨胀,性能就会下降。

posted @ 2021-05-15 23:38  一懒众衫小QAQ  阅读(143)  评论(0编辑  收藏  举报