Synchronization 锁

Synchronization 锁

基础知识点

  • synchronized 锁信息保存在对象头当中。
  • Monitor Record 是每个线程的私有数据结构,synchronized的锁对象都有一个monitor都会和线程的Monitor Record 关联,
  • 同步代码块是通过monitor entermonitor exit 实现,monitor enter 会插入到同步代码块的头部,monitorexit 会插入到方法的结尾或者出现异常的地方。当线程执行monitorenter 与对象头的monitor关联成功后,就成功获取到锁。

  • 偏向锁:也叫做无锁,是指当一个线程获取到锁,会在对象头中保存当前线程的ID,这时候的锁状态被称为偏向锁,偏向锁没有使用CAS的操作加锁和解锁。只是简单的测试一下对象头中是否还存在Monitor Record。测试成功,则表示当前线程已经获取到锁,失败则判断锁的标识是否设置为1,既偏向锁。没有设置的话使用CAS竞争锁,设置了就尝试使用CAS将对象头的偏向锁指向自身线程(此时会引发竞争,偏向锁会升级为轻量级锁)。。

  • 自旋锁: 当第二个线程尝试获取已经被其他线程占有的锁时,会尝试自选等待,自旋的过程是do-while循环,依赖于CAS实现,出现自旋是在清轻量级锁即将升级重量级锁的过程中。

  • 轻量级锁

    • 引入背景:这种锁实现的背后基于这样一种假设,即在真实的情况下我们程序中的大部分同步代码一般都处于无锁竞争状态(即单线程执行环境),在无锁竞争的情况下完全可以避免调用操作系统层面的重量级互斥锁,取而代之的是在monitorenter和monitorexit中只需要依靠一条CAS原子指令就可以完成锁的获取及释放。当存在锁竞争的情况下,执行CAS指令失败的线程将调用操作系统互斥锁进入到阻塞状态,当锁被释放的时候被唤醒
    1. 当对象处于无锁状态时(RecordWord值为HashCode,状态位为001),线程首先从自己的可用moniter record列表中取得一个空闲的moniter record,初始Nest和Owner值分别被预先设置为1和该线程自己的标识,一旦monitor record准备好然后我们通过CAS原子指令安装该monitor record的起始地址到对象头的LockWord字段,如果存在其他线程竞争锁的情况而调用CAS失败,则只需要简单的回到monitorenter重新开始获取锁的过程即可。
    2. 对象已经被膨胀同时Owner中保存的线程标识为获取锁的线程自己,这就是重入(reentrant)锁的情况,只需要简单的将Nest加1即可。不需要任何原子操作,效率非常高。
    3. 对象已膨胀但Owner的值为NULL,当一个锁上存在阻塞或等待的线程同时锁的前一个拥有者刚释放锁时会出现这种状态,此时多个线程通过CAS原子指令在多线程竞争状态下试图将Owner设置为自己的标识来获得锁,竞争失败的线程在则会进入到第四种情况(4)的执行路径。
    4. 对象处于膨胀状态同时Owner不为NULL(被锁住),在调用操作系统的重量级的互斥锁之前先自旋一定的次数,当达到一定的次数时如果仍然没有成功获得锁,则开始准备进入阻塞状态,首先将rfThis的值原子性的加1,由于在加1的过程中可能会被其他线程破坏Object和monitor record之间的关联,所以在原子性加1后需要再进行一次比较以确保LockWord的值没有被改变,当发现被改变后则要重新monitorenter过程。同时再一次观察Owner是否为NULL,如果是则调用CAS参与竞争锁,锁竞争失败则进入到阻塞状态。
  • 重量级锁: 是进行了系统调用、用户态代码发起系统调用向内核申请mutex互斥量,CPU接着执行进入内核代码、内核代码进行了后续的高低电位锁总线等一系列硬件操作,最后互斥的设置了一个内存中的标志位称为互斥锁,然后把这个锁返回给了用户态代码。这个过程中当然是有上下文/用户态内核态切换的。会造成线程阻塞,并将线程上下文切换,记录当前线程的状态,然后挂起,切换到其他线程,还包括从用户态到内核态的转化。如果频繁的出现加重量级锁,线程阻塞,是非常浪费CPU的性能,计算浪费在线程状态切换中。

  • JVM对象包含的信息:

  1. mark Word 存储对象的HashCode 和锁信息
  2. meta Data 对象的class文件的元数据空间指针
  3. 数组长度是数组对象包含的信息
  4. 中间为对象中的实例数据 例如 int id,String name 等
  5. 最下面是填充位,这个地方不存储任何数据,因为在内存中是8个字节64位一行,为了寻址算法定位对象更加高效,避免偏移计算。Java底层会使用对象存储空间/8 向上取整。例如 对象所有信息是20字节,那么对象的真实空间为 20 / 8 余数是4 结果是20 + 4 = 24
    JVM对象包含的信息
  • 对象头信息
  1. 对象锁变化标识位主要集中在后三位,后两位是锁标识位,倒数第三位是是否偏向锁位
  2. 初始化JVM的四秒内偏向锁标识位为0,四秒过后所有创建的对象都自动标记为偏向锁,
    64位对象锁标识变化过程
  • 锁升级过程图:
    锁升级
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值