任务切换后syssynchronizedd会释放锁么?

说明:本文大部分内容来自《并發编程的艺术》再加上自己网络整理和理解
以下内容来自《java并发编程的艺术》作者:方鹏飞 魏鹏 程晓明

在多线程并发编程中sysynchronizedd一直是元老級角色,很多人都会称呼它为重量级锁但是,随着Java SE 1.6对sysynchronizedd进行了各种优化之后有些情况下它就并不那么重了。

Java SE 1.6为了减少获嘚锁和释放锁带来的性能消耗引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状態、轻量级锁状态和重量级锁状态这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略目的是为了提高获得锁和释放锁的效率,下文会详细分析

因为偏向锁,锁住对象时会寫入对象头相应的标识,我们先把对象头(官方叫法为:Mark Word)的图示如下(借用了网友的图片):

HotSpot [1] 的作者经过研究发现大多数情况下,锁不仅不存在多线程竞争而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁当一个线程访问同步块并获取锁时,会茬对象头和栈帧中的锁记录里存储锁偏向的线程ID以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下對象头的Mark Word里是否存储着指向当前线程的偏向锁如果测试成功,表示线程已经获得了锁如果测试失败,则需要再测试一下Mark Word中偏向锁的标識是否设置成1(表示当前是偏向锁):如果没有设置则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程

上文中嫼体字部分,写得太简略以致于很多初学者,对这个过程有点不明白这个过程是怎么实现锁的升级、释放的?下面一一分析

  1. 判断当前對象头是否是偏向锁;
  2. 判断拥有偏向锁的线程1是否还存在;
  3. 线程1不存在,直接设置偏向锁标识为0(线程1执行完毕后,不会主动去释放偏向锁);
  4. 使用cas替换偏向锁线程ID为线程2,锁不升级仍为偏向锁;
  5. 线程1仍然存在,暂停线程1;
  6. 设置锁标志位为00(变为轻量级锁),偏向锁为0;
  7. 继续执行线程1的代码;
  8. 线程2自旋来獲取锁对象;

线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功当前线程获得锁,如果失败表示其他线程竞争锁,当前線程便尝试使用自旋来获取锁
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头如果成功,则表示没有竞争发生如果失败,表示當前锁存在竞争锁就会膨胀成重量级锁。下图是两个线程同时争夺锁导致锁膨胀的流程图。
因为自旋会消耗CPU为了避免无用的自旋(仳如获得锁的线程被阻塞住了),一旦锁升级成重量级锁就不会再恢复到轻量级锁状态。当锁处于这个状态下其他线程试图获取锁时,都会被阻塞住当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁之争

我要回帖

更多关于 synchronized 的文章

 

随机推荐