java锁的升级和对比
Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在 Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状
态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏 向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高
获得锁和释放锁的效率. ------来自《Java并发编程的艺术》
1. 对象头
synchronized用的锁是存在对象头中的。
对象头的存储
-
数组类型 ----> 3个字宽 -
非数组类型 ----> 2个字宽 -
32位虚拟机中一个字宽为4字节(32bit)
32位jvm中Mark Word的默认存储结构为
Mark Word的状态变化
2.偏向锁
HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。
----来自《Java并发编程的艺术》
加锁和释放锁是有一定开销的。
首先线程在竞争到资源后进行加锁,其他未竞争到资源的线程进入阻塞队列 -----> 创建了阻塞队列而且涉及到进程的加入
然后在释放锁时,还需要对阻塞队列中的进程进行处理
而偏向锁的意思是在对象头中存入标记即在上节图中所见,然后当线程访问这个对象时,进行标志位判断,如果正确直接进行访问,省去了加锁过程,jvm默认开启偏向锁。
偏向锁的撤销
偏向锁的撤销会在其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销需要等到全局安全点(没有正在执行的字节码)。
步骤
暂停拥有偏向锁的线程 , 检查线程是否还活着 不活动---->对象头设置为无锁状态 活着----> 执行拥有偏向锁的栈,遍历偏向锁对象的锁记录,栈中的锁记录和对象头Mark Word要么重新偏向于其他线程,要么恢复到无锁或者标记对象不想喝作为偏向锁,最后唤醒暂停的线程。
2. 轻量级锁
加锁
首先会在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word 复制到锁记录中 ----->Displaced Mark Word
线程通过CAS将对象头中的Mark Word替换成指向锁记录的指针, 成功-----> 获取锁 失败-----> 尝试自旋获取锁
这种方式比在阻塞队列中等待要快。
解锁
通过原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功 ----> 没有竞争发生, 失败 ----> 存在竞争 膨胀成重量级锁
竞争烈度高,线程自旋占用CPU较高 —> 升级为重量锁 放到阻塞队列中等通知,减少CPU资源消耗
锁的优缺点对比
参考资料
《Java并发编程的艺术》
|