• 描述一下锁的四种状态及升级过程?


     

    1、锁的四种状态

    无锁、偏向锁、轻量级锁、重量级锁

    2、Java对象头描述

    以下为32位对象头描述
    在这里插入图片描述以下为64位对象头描述
    在这里插入图片描述

    3、锁的升级过程(Synchronized加锁/膨胀流程)

    1)简单过程如下图

    在这里插入图片描述

    2)详细过程

    当线程访问同步代码块时,首先判断当前锁状态是否为可偏向状态(对象头中偏向锁=1,锁标志=01)
    在JDK1.6以上默认开启,开启后程序启动几秒后才会被激活

    (1)偏向锁

    如果是可偏向状态,检查MarkWord存储的是否是当前线程ID

    • 是,获得偏向锁,执行同步代码块
    • 不是,CAS操作竞争锁,替换线程ID
      • 替换成功,MarkWord的线程ID设置为当前线程ID(线程复用),执行同步代码块
      • 替换失败,锁撤销,升级为轻量级锁
        同一类对象多次撤销升级达到阈值20,则偏向锁认为,后面的锁需要重新偏向新的线程(批量重偏向)
        如果阈值达到40次,则偏向锁认为偏向锁撤销过于频繁,后面直接使用轻量级锁

    (2)轻量级锁

    升级为轻量级锁的情况

    • 如果不是可偏向状态,直接升级为轻量级锁
    • 偏向锁撤销次数过多

    加锁时,会在当前线程栈帧中划出一块空间,作为该锁记录,并且将锁对象MarkWord复制到该锁记录中,CAS操作将MarkWord更新为该锁记录的指针,锁记录中的owner指针指向对象头的MarkWord。

    • 更新成功,MarkWord锁标志位为00,表示轻量级锁状态
    • 更新失败,检查锁对象MarkWord是否指向当前线程栈帧中的锁记录
      • 是,表示锁重入,在当前线程栈帧中锁记录+1
      • 否,自旋等待(默认10次),等待次数达到阈值,升级为重量级锁

    (3)重量级锁

    升级为重量级锁的情况

    • 竞争加剧,CAS自旋到一定次数升级为重量级锁
    • 自旋线程数超过CPU核数的一半, 1.6之后,加入自适应自旋Adapative Self Spinning,JVM自己控制

    获取锁成功,进入EntryList(获取锁的缓冲区、入口)

    • 在调用wait方法后,会进入等待唤醒队列(WaitSet)等待
    • 在调用notify方法后,则可能进入EntryList
      获取锁失败,进入一个等待拿锁队列(cxq)等待

    具体重量级锁加锁过程:
    1、分配⼀个ObjectMonitor对象,把MarkWord锁标志置为‘10’,然后MarkWord存储指向ObjectMonitor对象的指针。ObjectMonitor对象有两个队列和⼀个指针,每个需要获取锁的线程都包装成ObjectWaiter对象
    2、多个线程同时执行同⼀段同步代码时,ObjectWaiter先进⼊EntryList队列,当某个线程获取到对象的monitor以后进⼊Owner区域,并把monitor中的owner变量设置为当前线程,同时monitor中的计数器count+1

    说明:
    monitor:每个Java对象都有一把锁,称为内部锁或monitor锁
    owner,指向的是当前获得线程的地址,用来判断当前锁是被哪个线程持有

    4、拓展

    1)synchronized效率低?

    用户态:偏向锁、轻量级锁
    内核态:重量级锁

    首先来了解下synchronized重量级锁实现原理?
      通过对象内部的一个叫做监视器锁(monitor)来实现的,监视器锁本质又是依赖于底层的操作系统的 Mutex Lock(互斥锁)来实现的。而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高。
      在JDK6以前,只有重量级锁,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间。
      在JDK6中,为了提高性能,引入了偏向锁和轻量级锁。

    2)为什么要有偏向锁?

    大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了减少线程获得锁的代价,所以引入了偏向锁

    3)为什么要有重量级锁?

    自旋锁消耗CPU资源,重量级锁有等待队列,不会消耗CPU资源

    4)偏向锁是否一定比自旋锁效率高?

    不一定,在多线程竞争情况下,偏向锁会涉及锁撤销,这时候应该直接使用自旋锁

    5)锁重入

    重入次数必须记录,才能知道要解锁几次

    • 轻量级锁,记录在线程栈,每插入一次,LockRecord+1
    • 重量级锁,记录在ObjectMonitor字段上

    6)Hopspot对象头就是MarkWord?

    不是的,Hopspot对象头主要包括两部分数据:MarkWord(标记字段) 和 classPointer(类指针)

    7)锁可以降级?

    不行的,是一个不可逆的过程,主要是为了提高获得锁和释放锁的效率

    8)锁对比,适用场景?

    偏向锁:适用于一个线程,不会有锁消耗,锁撤销
    轻量级锁:适用于多个线程竞争,但同步代码块执行快的情况下,因为自旋会消耗CPU
    重量级锁:适用于多个线程竞争,但同步代码块执行慢的情况下,不消耗CPU,可以提高吞吐量

  • 相关阅读:
    MyBatis框架中各种参数类型绑定的方式
    卷积神经网络工作原理的直观理解
    openGauss学习笔记- - -初始与使用技巧
    Jetson Nano 部署(2):DeepStream介绍
    Leetcode力扣 MySQL数据库 1811 寻找面试候选人
    app小程序手机端Python爬虫实战04-u2自动化工具基本操作-操作设备
    Elasticsearch
    亚马逊按关键字搜索商品 API 返回值说明
    计算机视觉中的可解释性分析
    pdd.order.information.get拼多多订单详情接口代码对接教程
  • 原文地址:https://www.cnblogs.com/huozhonghun/p/17185298.html