JAVA锁优化

最近在整理自己的技术体系,关于并发编程,入门级知识重量级锁Synchronized,接下来试着解释这块知识。

为什么说Synchronized是重量级锁

jdk1.6之前,还没有引入“偏向锁”和”轻量级锁“,synchronized是依赖monitorenter指令和monitorexit指令实现的,而这个jvm指令集是依赖操作系统内核来完成的,就会涉及到用户态与内核态的数据传递,是比较耗性能的,所以,我们前辈称之为重量级锁。

锁存放在哪里

对象的内存布局如下

Java 锁CLH消耗CPU资源 java锁的优化_用户态

如图,对象头markword有锁的标记位,可以表示当前对象的锁的四种状态,无锁、偏向锁、轻量级锁、重量级锁。

Jdk1.6是如何优化Synchronized的,让它“轻量级了”?

上面我们提到了,jdk1.6引入了锁的升级过程,锁分以下几种状态,

  1. 无锁 (对象的初始状态)
  2. 偏向锁
  3. 轻量级锁
  4. 重量级锁

那么锁的升级过程是什么样的?

1.1 最开始是无锁状态

1.2 线程进入同步方法块,将自己的线程id通过CAS操作塞进对象头MarkWord中,升级为偏向锁

如果对象头MarkWord有偏向锁,
如果这个偏向锁threadId是自己,是直接使用该锁。
如果不是,
则判断指向的线程是否存活,如果该线程不处于活跃状态,则将对象头置为无锁,如果还存活,则等待它执行完成后释放锁,同时升级成轻量级锁.

偏向锁,自旋次数少,大部分的synchronized锁,都只是在无锁和偏向锁之间徘徊,消耗性能极低。

1.3 二个线程竞争cas自己的线程id到锁对象的markword中,则升级为轻量级锁(自旋锁)。

轻量级锁,自旋次数多,cpu消耗比较高,好在它停留在jvm层面的自旋操作,不涉及到用户态与内核态的切换.

1.4 自旋限定次数,如果还是失败,则竞争锁失败,升级为重量级锁

重量级锁,使用monitorenter和monitorexit命令,依赖操作系统内存,涉及到用户态与内核态切换,比较耗费性能,不建议锁升级到此。