Java并发2:synchronized 原理

0、前言

不管是轻量级锁还是重量级锁,其语法都是synchronized。
锁一共有4个状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁、重量级锁;
锁可以升级但是不可以降级;

CAS

  • CAS使用了三个基本操作数:内存地址V,旧的预期值A,要修改的新值B。只有当A与V中保存的实际值相同时才会将V保存的值修改为B;
  • Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新

1、Java对象头

在这里插入图片描述

2、偏向锁

偏向锁的来源是因为Hotsopt的作者研究发现大多数情况下,锁不仅不存在多线程竞争,而且总是由统一线程多次获得,而线程的阻塞和唤醒需要CPU从用户态转为核心态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作,为了让线程获得锁的代价更低而引入了偏向锁。

偏向锁的获取

  1. 获取对象头的Mark word,判断是否处于可偏向状态
  2. 若是可偏向状态,通过CAS操作把当前线程的ID写入到Mark Word;若是CAS成功设置偏向锁标记为1将线程ID写入Mark Word;
  3. 若是CAS失败说明当前其他线程获得了偏向锁,存在锁竞争,检查Mark Word的线程ID是否和自己相等,若相等则不需要再次获得锁可以直接执行同步代码块;
  4. 若不相等,需要将已经获得偏向锁的线程中的偏向锁撤销掉并升级为轻量级锁。(偏向锁的撤销,需要等到到达全局安全点即CPU没有正在执行的字节。此时获得偏向锁的线程被挂起)

偏向锁的撤销

  1. 原获得偏向锁的线程如果已经退出了临界区,也就是同步代码块执行完了,那么这个时候会把对象头设置成无锁状态,同时正在争抢锁的线程可以基于 CAS 重新偏向当前线程。
  2. 如果原获得偏向锁的线程的同步代码块还没执行完,处于临界区之内,这个时候会把原获得偏向锁的线程升级为轻量级锁后继续执行同步代码块。

2、轻量级锁

偏向锁是只允许一个线程获得锁,轻量级锁允许多个线程获得锁,但是只允许他们顺序拿锁,不允许出现竞争,也就是拿锁失败的情况。
获取轻量级锁的过程
在这里插入图片描述
在这里插入图片描述

  1. 线程1在执行代码块之前,JVM现在当前栈帧创建一个空间用来存储锁记录,再使用CAS把对象头的Mark Word复制到该锁记录。若成功则获得锁,失败执行2.
  2. 线程自旋,自旋成功则获得锁。自旋失败,锁膨胀成为重量级锁,锁标志位变为10线程阻塞进入3.
  3. CAS执行失败说明期间有线程尝试获得锁并自旋失败,轻量级锁升级为了重量级锁,此时释放锁之后,还要唤醒等待的线程.

3、自旋锁

4、重量级锁

当Thread1加轻量级锁失败,此时thread0持有obj的轻量级锁。
这时thread1加轻量级锁失败,进入锁膨胀:
在这里插入图片描述

  1. 为obj申请Monitor锁,让obj指向重量级锁地址
  2. thread1进入Monitor的EntryList阻塞
  3. 当thread0退出同步块解锁时,使用cas将Mark Word的值恢复给对象头,失败。这是会进入重量级解锁流程,即按照Monitor地址找到Monitor对象,设置Owner为null,唤醒EntryList中的阻塞进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值