每一个对象都有一个监视器锁(monitor),当monitor被占用时标识对象已经被锁定了
java对象在内存中的存储分为以下几部分
Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。 而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高
,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因
临界区: 需要获取到对象监视器锁(monitor)才能执行的部分代码。
本意为线程交替执行临界区代码,并不存在竞争的情况,如果同一时间多个线程竞争访问同一个锁,轻量级锁会膨胀成重量级锁
由对象头的情况来看,当轻量级锁的情况下,对象头内存放的 指向站内锁记录的指针
什么是锁记录:
1.轻量级锁,会在当前的线程栈中,分配一块名字叫Lock Record(锁记录)
的空间,用于拷贝锁对象的
mark word
2.当轻量级锁释放的时候,锁记录中的 mark word
会重新写会锁对象中。
加锁成功后:
1.对象头mark word 标记为00
2.锁记录是原先对象头中mark word的拷贝,并且锁记录中的owner执行对象头的mark word
3.对象头中原先mark word的内容是指向锁记录的指针
如果只有一个线程竞争锁的情况下,jvm只需要使用偏向锁就可以了。因为偏向锁更加的高效,由上面的介绍可以看到,加偏向锁时,会涉及到 mark word
内容的拷贝,以及多个cas操作。偏向锁更加的方便,只需要做一次cas 轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。
偏向锁不会主动释放,偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态
上面介绍了重量级锁、轻量级锁、偏向锁,其实轻量级锁和偏向锁都是jvm对Synchronized的一个优化(当竞争还不是很紧张的时候,采用轻巧一点的办法)
当然jvm还有别的优化,比如:
因为获取重量级锁时候,线程上下文切换会是效率低下的原因之一。所以,在当前线程去竞争重量级锁时,使用自旋的方式,不放弃cpu资源,如果能在自旋期间获取到了锁也是减少了线程上下文的切换。
由于获取到锁的时间不确定性,所以jvm对锁自旋的时间采用了自适应的方式。即:如果上次在自旋期间获取到锁了,那么这次自旋的时间的适当加长,如果上次没有在自旋期间加到锁,那么这次自旋的时间适当减短。