JVM锁优化以及区别

偏向所锁,轻量级锁都是乐观锁,重量级锁是悲观锁。 java

首先简单说下先偏向锁、轻量级锁、重量级锁三者各自的应用场景:优化

  • 偏向锁:只有一个线程进入临界区;
  • 轻量级锁:多个线程交替进入临界区
  • 重量级锁:多个线程同时进入临界区。

还要明确的是,偏向锁、轻量级锁都是JVM引入的锁优化手段,目的是下降线程同步的开销。好比如下的同步代码块:spa

synchronized (lockObject) { // do something }

上述同步代码块中存在一个临界区,假设当前存在Thread#1和Thread#2这两个用户线程,分三种状况来讨论:线程

  • 状况一:只有Thread#1会进入临界区;
  • 状况二:Thread#1和Thread#2交替进入临界区;
  • 状况三:Thread#1和Thread#2同时进入临界区。

sychronized锁优化解释1:指针

上述的状况一是偏向锁的适用场景,此时当Thread#1进入临界区时,JVM会将lockObject的对象头Mark Word的锁标志位设为“01”,同时会用CAS操做把Thread#1的线程ID记录到Mark Word中,此时进入偏向模式。所谓“偏向”,指的是这个锁会偏向于Thread#1,若接下来没有其余线程进入临界区,则Thread#1再出入临界区无需再执行任何同步操做。也就是说,若只有Thread#1会进入临界区,实际上只有Thread#1初次进入临界区时须要执行CAS操做,之后再出入临界区都不会有同步操做带来的开销。code

然而状况一是一个比较理想的状况,更多时候Thread#2也会尝试进入临界区。若Thread#2尝试进入时Thread#1已退出临界区,即此时lockObject处于未锁定状态,这时说明偏向锁上发生了竞争(对应状况二),此时会撤销偏向,Mark Word中再也不存放偏向线程ID,而是存放hashCode和GC分代年龄,同时锁标识位变为“01”(表示未锁定),这时Thread#2会获取lockObject的轻量级锁。由于此时Thread#1和Thread#2交替进入临界区,因此偏向锁没法知足需求,须要膨胀到轻量级锁。对象

再说轻量级锁何时会膨胀到重量级锁。若一直是Thread#1和Thread#2交替进入临界区,那么没有问题,轻量锁hold住。一旦在轻量级锁上发生竞争,即出现“Thread#1和Thread#2同时进入临界区”的状况,轻量级锁就hold不住了。 (根本缘由是轻量级锁没有足够的空间存储额外状态,此时若不膨胀为重量级锁,则全部等待轻量锁的线程只能自旋,可能会损失不少CPU时间)资源

 

sychronized锁优化解释2:同步

一个对象刚开始实例化的时候,没有任何线程来访问它的时候。它是可偏向的,意味着,它如今认为只可能有一个线程来访问它,因此当第一个线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁。偏向第一个线程,这个线程在修改对象头成为偏向锁的时候使用CAS操做,并将对象头中的ThreadID改为本身的ID,以后再次访问这个对象时,只须要对比ID,不须要再使用CAS在进行操做。hash

一旦有第二个线程访问这个对象,由于偏向锁不会主动释放,因此第二个线程能够看到对象时偏向状态,这时代表在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,若是挂了,则能够将对象变为无锁状态,而后从新偏向新的线程,若是原来的线程依然存活,则立刻执行那个线程的操做栈,检查该对象的使用状况,若是仍然须要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。若是不存在使用了,则能够将对象回复成无锁状态,而后从新偏向。     

轻量级锁认为竞争存在,可是竞争的程度很轻,通常两个线程对于同一个锁的操做都会错开,或者说稍微等待一下(自旋),另外一个线程就会释放锁。 可是当自旋超过必定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程之外的线程都阻塞,防止CPU空转。

 

 补充:轻量锁膨胀到重量锁有两个条件

1.等待的线程自旋超过必定次数。

2.持有锁的线程CAS释放锁,可是失败。

解释:

1.很显然,自旋超过必定次数会消耗CPU资源,干脆阻塞。

2.当线程CAS释放锁的时候,比较对象头中的mark word是否指向本线程的lock record

(此处认为还要比较对象头中markword是否和线程lock record中的displace mark word相同,我认为是没道理的,由于此时lock record中的displaced存储的是对象的hashcode,而对象的mark word中存储的倒是指向lock record的指针,所以不可能相同。并且此处的CAS指得就是用hashcode替换指针)。当发现CAS失败的时候,说明什么?尽管有一个线程在自旋,可是仍是CAS失败,说明竞争比较频繁,所以升级成重量锁,阻塞其余线程,本身安安稳稳的释放掉后再唤醒其余线程,而后让其余线程竞争去。
相关文章
相关标签/搜索