Synchronized锁原理
前情提要
-
对于普通同步方法,锁是当前实例对象。
-
对于静态同步方法,锁是当前类的Class对象。
-
对于同步方法块,锁是Synchonized括号里配置的对象。
Synchonized在JVM里的实现原理,
JVM基于进入和退出Monitor对 象来实现方法同步和代码块同步
Monitor是有monitorenter和monitorexit指令的,线程每次执行到monitorenter,都会尝试获取对象的monitor,也就是对象的锁
锁的升级
Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。
无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级
1. 偏向锁
为什么有偏向锁概念?
为了减少获得锁和释放锁带来的性能消耗.
那下面来从减少消耗入手来讲
大多数情况下,锁不仅不存在多线程竞争,而且总是由同 一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁
你可以理解可重入锁,当进入拿到了锁再进入其它的就方便了,这里只是比喻。。不等同
哪些措施减少消耗的(线程ID的检查)
当一个线程访问同步块并 获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,下次进入直接测试有无ID记录,有就简单了直接盘它!
没有的话,先看看这是不是偏向锁,不是就用CAS竞争锁然后记录线程ID,是的话就用CAS直接记录自己的线程ID,
1.1 偏向锁的撤销
只要有其它线程竞争偏向锁,就会释放锁,具体流程如下:
线程1拿到偏向锁在执行,线程2来执行同步方法,先检查ID,没有,检查发现是偏向锁,CAS记录自己的线程ID,但在记录之前需要暂停线程1,然后把线程ID设置为空,然后就可以设置自己的了
1.2 偏向锁的意义
偏向锁,它的目的是消除数据在无竞争的情况下的同步原语,进一步提高程序的运行性能,不用CAS
2. 轻量级锁
2.1 加锁
-
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间
-
拿到锁记录指针。将对象头中的Mark Word复制到锁记录中,CAS替换那个markword
-
如果成功,当前线程获得锁,
-
如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
2.2 解锁
-
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头
-
如果成功,则表示没有竞争发生。
-
如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁
2.3 轻量锁的意义
轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量
3.锁的对比
锁 | 优点 | 缺点 | 使用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁无消耗,争取不用CAS | 竞争锁的情况会有撤销锁的消耗 | 适用单线程 |
轻量级锁 | 竞争的线程不会阻塞,利用CAS | CAS会自旋消耗CPU | 追求响应时间 |
重量级锁 | 不会消耗CPU,直接阻塞对方 | 线程会阻塞 | 追求吞吐量,不在乎时间和交互 |