并发编程离不开锁,然而每次遇到锁问题时都会谈锁色变,下面对锁实现做简单化描述,方便你们容易理解!java
在JAVA中锁一共有四种状态:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态(按从低到高顺序,锁着竞争状况逐渐升级)算法
JAVA中锁只能升级却不能降级,目的是为了提升得到锁和释放锁的效率。 编程
在HopSpot虚拟机中,对象在内存存储中分为3部分:对象头(Header)、实例数据(Instance Data) 和 对齐填充(Padding)。 想了解Java对象结构的详细信息请看:java对象结构安全
java对象头包含3部分信息,以下: 多线程
锁的状态保存在对象头的Mark Word中,以32位JDK为例: 并发
在无多线程竞争锁的状况下,为了让同一线程得到锁的代价更低而引入了偏向锁。学习
CAS是一种无锁算法,CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。 测试
偏向锁使用了一种等到竞争出现才释放锁的机制,因此当其余线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。 偏向锁的撤销,须要等待全局安全点(在这个时间点上没有正在执行的字节码),它会先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。优化
线程在执行同步块以前,JVM会先在当前线程的栈帧中建立用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录(Lock record)中,官方成为Displaced Mark Word。而后尝试使用CAS算法将对象头中的Mark Word替换为指向锁记录的指针。若是成功,当前线程得到锁,若是失败,表示其余线程竞争锁,当前线程便尝试使用自旋来获取锁。操作系统
轻量级锁解锁时,会使用原子的CAS操做将Displaced Mark Word替换回对象头,若是成功,则表示没有竞争发生。若是失败,表示当前存在竞争,锁就会膨胀成重量级锁。
由于自旋会消耗CPU,为了不无用的自旋(好比得到锁的线程被阻塞了),一旦锁升级成重量级锁,就不会再恢复到轻量级锁状态。其余线程试图获取锁时,会被阻塞住,当持有锁的线程释放以后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁竞争。
在多线程并发编程中synchronized一直是元老级角色,不少人称呼它为“重量级锁”。synchronized是经过对象内部的一个叫作监视器锁(monitor)来实现的,可是监视器锁本质又是依赖于底层操做系统的Mutex Lock来实现的,而操做系统实现线程之间的切换就须要从用户状态切换到核心状态,这个成本很高,状态之间转换须要相对比较长的时间,这就是synchronized效率低的缘由。所以,这种依赖于操做系统Mutex Lock来实现的锁,咱们称之为“重量级锁”。
在JDK1.6后,synchronized获得了种种优化后,在某些状况下已经没那么重了。
注:本文主要观点来源于《java并发编程的艺术》和我的学习网上的文章的一些总结。