zoukankan      html  css  js  c++  java
  • JVM锁优化以及区别

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

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

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

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

    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操作,以后再出入临界区都不会有同步操作带来的开销。

    然而情况一是一个比较理想的情况,更多时候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在进行操作。

    一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象时偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。如果不存在使用了,则可以将对象回复成无锁状态,然后重新偏向。     

    轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止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失败,说明竞争比较频繁,因此升级成重量锁,阻塞其他线程,自己安安稳稳的释放掉后再唤醒其他线程,然后让其他线程竞争去。


  • 相关阅读:
    一张图帮你分清scroll、offset、client
    js两种显示日期的方法
    理解js的全局变量和局部变量
    中文输入+英文标点+快速编辑Markdown文本+Sublime+Snippet
    Markdown 使用方法
    get和post的区别
    js对象属性方法大总结(收集)
    bfc (收集的)
    客户端网页编程知识总结
    html学习总结
  • 原文地址:https://www.cnblogs.com/cangqiongbingchen/p/11009844.html
Copyright © 2011-2022 走看看