对synchronized底层的了解, 锁的四种状态

首先看一个简单的代码


public class SynchronizedDemo {
	public static void main(String[] args) {
        synchronized (SynchronizedDemo.class) {
        }
        method();
    }

    private static void method() {
    }
}

通过 javap -v SynchronizedDemo.class 命令查看class文件
在这里插入图片描述
看到了3、5行的命令 monitor
这是因为执行同步方法和同步代码块是,首先要获得该对象的锁也就是该对象的监视器monitor才能进入,如果没有获得该对象的监视器进入BLOCKED阻塞状态;
在这里插入图片描述
以上可以看出获得对象的监视器(monitor)就可以获得对象锁,那么这个对象锁到底怎么理解呢 ,这里引入了对象头的概念 , 这里的对象锁相当于一个标记,获取到对象锁后将对象锁存放到对象头的markword中,具体的markword可以另外了解下
在这里插入图片描述
mark word 中存储了 锁的状态(锁的状态下面会说),获得该锁的线程ID等;

总结:
synchronize获取到对象锁就是获取到对象的monitor,进入对象同步块,将锁的标志信息存入到该对象头的mark word中
锁的四种状态
  1. 无锁状态
  2. 偏向锁
  3. 轻量级锁
  4. 重量级锁
偏向锁

大多数情况下,锁可能都由一个线程锁获得,为了让线程获取锁的代价更小引入了偏向锁:
首先获取锁需要获取到对象的monitor(监听器);获取到对象后将偏向锁的ID存入到对象头中的MARK word中和栈帧锁记录当中;在以后获取锁时,不需要通过CAS竞争锁,只需通过对象头中的偏向锁ID 指向当前线程的偏向锁

再次获取锁的几种情况:
1.id匹配直接获取锁;
2.匹配失败,如果锁状态为无锁状态;通过cas竞争获取锁
3.如果锁状态为偏向锁状态,有第二个线程访问这个对象出现线程竞争;(偏向锁不会主动释放锁)操作系统检测原来只有该线程的锁状态,
如果不存活;修改为无锁状态,重新指向新的线程;
如果存活,检测该对象的使用情况,
如果仍然需要持有该偏向锁,则该偏向锁升级为轻量级锁(锁升级);不需要保持偏向锁,让对象恢复为无锁状态,进行重新偏向

轻量级锁

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

锁再次升级:b在线程竞争时,发现线程已经让a占用,b线程自旋,等待a线程释放锁;当自旋结束后,发现a线程还是没有释放或是c线程占用。则b线程试图将轻量级锁升级为重量级;
在这里插入图片描述

重量级

轻量级锁膨胀之后,就升级为重量级锁了。重量级锁是依赖对象内部的monitor锁来实现的,而monitor又依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被成为互斥锁。

synchronized的执行过程:

  1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁
  2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1
  3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。
  4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁
  5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
  6. 如果自旋成功则依然处于轻量级状态。
  7. 如果自旋失败,则升级为重量级锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`synchronized` 关键字的底层实现和获取与释放过程涉及到了 Java 虚拟机(JVM)、Java 对象头和操作系统等多个层面。 当一个线程访问一个被 `synchronized` 修饰的方法或代码块时,JVM 会首先尝试获取这个同步。如果这个没有被其他线程占用,则该线程可以顺利获取,并进入临界区。如果这个已经被其他线程占用,则该线程会进入阻塞状态,直到获取为止。 在 Java 对象头中,有一个字段用于存储这个对象的 Mark Word,这个字段中的一部分用于存储的信息,例如状态、持有的线程 ID 等。当一个线程获取时,会将这个状态设置为“定”,同时将持有的线程 ID 记录在 Mark Word 中。当这个线程释放时,会将状态设置为“未定”,并清除持有的线程 ID。 在操作系统层面,获取和释放需要通过操作系统提供的原语来实现,例如 Linux 中的 futex。当线程在获取时,如果已经被其他线程占用,则它会进入阻塞状态。操作系统会将这个线程加入到等待队列中,并在被释放时通知到这个线程,使其重新竞争。当线程释放时,操作系统会将等待队列中的一个或多个线程唤醒,并将的所有权转移到其中一个线程手中。 总的来说,`synchronized` 关键字的实现涉及到了多个层面的细节,包括 Java 虚拟机、Java 对象头和操作系统等。了解底层实现原理有助于我们更好地理解和使用机制,从而编写出更加高效和健壮的多线程程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值