JAVA之锁机制实现原理(简化版)

并发编程离不开锁,然而每次遇到锁问题时都会谈锁色变,下面对锁实现做简单化描述,方便你们容易理解!java

1、绪论

在JAVA中锁一共有四种状态:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态(按从低到高顺序,锁着竞争状况逐渐升级)算法

JAVA中锁只能升级却不能降级,目的是为了提升得到锁和释放锁的效率。 编程

2、对象头的介绍

在HopSpot虚拟机中,对象在内存存储中分为3部分:对象头(Header)、实例数据(Instance Data) 和 对齐填充(Padding)。 想了解Java对象结构的详细信息请看:java对象结构安全

java对象头包含3部分信息,以下: 多线程

锁的状态保存在对象头的Mark Word中,以32位JDK为例: 并发

3、锁状态的介绍

一、偏向锁

在无多线程竞争锁的状况下,为了让同一线程得到锁的代价更低而引入了偏向锁。学习

1.一、偏向锁的获取过程
  • (1) 访问Mark Word中偏向锁的标识是否设置成1,即锁的标志为是否为01——确认为可偏向状态。
  • (2) 若是为可偏向状态,则测试线程ID是否指向当前线程,若是是,进入步骤(5),不然进入步骤(3)。
  • (3) 若是线程ID并未指向当前线程,则经过CAS操做竞争锁。若是竞争成功,则将Mark Word中线程ID设置为当前线程ID,而后执行(5);若是竞争失败,执行(4)。
  • (4) 若是CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时得到偏向锁的线程被挂起,偏向锁升级为轻量级锁,而后被阻塞在安全点的线程继续往下执行同步代码。
  • (5) 执行同步代码。

CAS是一种无锁算法,CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。 测试

1.二、偏向锁的释放

偏向锁使用了一种等到竞争出现才释放锁的机制,因此当其余线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。 偏向锁的撤销,须要等待全局安全点(在这个时间点上没有正在执行的字节码),它会先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。优化

二、轻量级锁

2.一、轻量级锁加锁

线程在执行同步块以前,JVM会先在当前线程的栈帧中建立用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录(Lock record)中,官方成为Displaced Mark Word。而后尝试使用CAS算法将对象头中的Mark Word替换为指向锁记录的指针。若是成功,当前线程得到锁,若是失败,表示其余线程竞争锁,当前线程便尝试使用自旋来获取锁。操作系统

2.二、轻量级锁解锁

轻量级锁解锁时,会使用原子的CAS操做将Displaced Mark Word替换回对象头,若是成功,则表示没有竞争发生。若是失败,表示当前存在竞争,锁就会膨胀成重量级锁。

由于自旋会消耗CPU,为了不无用的自旋(好比得到锁的线程被阻塞了),一旦锁升级成重量级锁,就不会再恢复到轻量级锁状态。其余线程试图获取锁时,会被阻塞住,当持有锁的线程释放以后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁竞争。

三、重量级锁

在多线程并发编程中synchronized一直是元老级角色,不少人称呼它为“重量级锁”。synchronized是经过对象内部的一个叫作监视器锁(monitor)来实现的,可是监视器锁本质又是依赖于底层操做系统的Mutex Lock来实现的,而操做系统实现线程之间的切换就须要从用户状态切换到核心状态,这个成本很高,状态之间转换须要相对比较长的时间,这就是synchronized效率低的缘由。所以,这种依赖于操做系统Mutex Lock来实现的锁,咱们称之为“重量级锁”。

在JDK1.6后,synchronized获得了种种优化后,在某些状况下已经没那么重了。

4、锁的优缺点对比

注:本文主要观点来源于《java并发编程的艺术》和我的学习网上的文章的一些总结。

相关文章
相关标签/搜索