精华内容
下载资源
问答
  • 偏向锁未启动时,markword的最后3个bit是001 匿名偏向时,markword的最后3个bit是101 jdk8默认的偏向锁启动延迟是4s(BiasedLockingStartupDelay=4000) PS C:\Java_Study\wsc_shell> java -XX:+PrintFlagsFinal ...

    理论知识

    看了一篇博客(https://blog.csdn.net/weixin_45007916/article/details/107535746),其中有以下的图片:
    偏向锁未启动时,markword的最后3个bit是001
    匿名偏向时,markword的最后3个bit是101
    jdk8默认的偏向锁启动延迟是4s(BiasedLockingStartupDelay=4000)

    PS C:\Java_Study\wsc_shell> java -XX:+PrintFlagsFinal -version|findstr /i bias
    java version "1.8.0_221"
    Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
    Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
         intx BiasedLockingBulkRebiasThreshold          = 20                                  {product}
         intx BiasedLockingBulkRevokeThreshold          = 40                                  {product}
         intx BiasedLockingDecayTime                    = 25000                               {product}
         intx BiasedLockingStartupDelay                 = 4000                                {product}
         bool TraceBiasedLocking                        = false                               {product}
         bool UseBiasedLocking                          = true                                {product}
         bool UseOptoBiasInlining                       = true                                {C2 product}
    

    在这里插入图片描述
    在这里插入图片描述

    实验环境

    windows10 x64
    C:\Program Files\Java\jdk1.8.0_221\bin\java.exe

    偏向锁未启动

    对象头的最后1个byte是00000001,最后3个bit是001,
    在这里插入图片描述

    匿名偏向

    延迟4100ms让偏向锁启动之后,再打印对象obj的信息内存结构信息
    对象头的最后1个byte是00000101,最后3个bit是101,但是对象头中并未看到指向当前线程的指针
    在这里插入图片描述

    偏向锁中当前线程指针不为空

    此时的偏向锁指向当前线程的数据(对象头低54个bit)为,00000000 00000000 00000000 00000000 00000011 01010110 011010
    Epoch为00
    hex(int(‘000000000000000000000000000000000000001101010110011010’, 2))
    0xd59a
    0xd59a这个和当前线程怎么联系起来?
    在这里插入图片描述

    计算对象的hashcode会使得偏向锁失效-匿名偏向到无锁

    对象头的最后1个byte是00111000,最后3个bit是001,
    从对象头的第26个bit开始的31bit是hashcode值,即
    0011110 11110111 11111110 10001110

    计算 int(‘0011110111101111111111010001110’, 2)
    519569038
    和输出的obj.hashCode()一致
    在这里插入图片描述

    计算对象的hashcode会使得偏向锁失效-升级为轻量级锁

    对象头的最后1个byte是01011000,最后3个bit是000,
    对象头的前62个bit是00000011 00110000 11110110 010110
    hex(int(‘000000110011000011110110010110’, 2))
    ‘0xcc3d96’
    0xcc3d96和当前线程栈中的Lock Record怎么联系起来?

    在这里插入图片描述

    展开全文
  • 偏向锁

    千次阅读 2019-04-12 01:42:15
    注:本文翻译自...HotSpot支持存储释放偏向锁,以及偏向锁的批量重偏向和撤销。这个特性可以通过JVM的参数进行切换,而且这是默认支持的。Unlock状态下MarkWord的一个比特位用于标识该对象偏向锁是否被使用...

    注:本文翻译自【Evaluating and improving biased locking in the HotSpot virtual machine】,译者水平有限,如词不达意,请告之!

    HotSpot支持存储释放偏向锁,以及偏向锁的批量重偏向和撤销。这个特性可以通过JVM的参数进行切换,而且这是默认支持的。Unlock状态下MarkWord的一个比特位用于标识该对象偏向锁是否被使用或者是否被禁止。如果该bit位为0,则该对象未被锁定,并且禁止偏向;如果该bit位为1,则意味着该对象处于以下三种状态:

    • 匿名偏向(Anonymously biased)
      在此状态下thread_ptr为NULL(0),意味着还没有线程偏向于这个锁对象。第一个试图获取该锁的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程。这是允许偏向锁的类对象的初始状态。
    • 可重偏向(Rebiasable)
      在此状态下,偏向锁的epoch字段是无效的(与锁对象对应的klass的mark_prototype的epoch值不匹配)。下一个试图获取锁对象的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程。在批量重偏向的操作中,未被持有的锁对象都被至于这个状态,以便允许被快速重偏向。
    • 已偏向(Biased)
      这种状态下,thread ptr非空,且epoch为有效值——意味着其他线程正在只有这个锁对象。

    基于偏向锁对象需要使用hashcode字段作为偏向线程id标识的事实,被hash的对象不可被用作偏向锁。对于允许偏向的对象在进行hashcode计算时,首先要吊销(revoke)所有的偏向(不管是有效的还是无效的),然后使用CAS将计算好的hashcode值放到MarkWord中,尽管这仅仅适用于“identity hashcode(使用Object类的hashcode()方法进行计算)”。普通Java类型hashcode的计算需要重载Object的hashcode()方法,但不必要去显示调用这个方法;因此,对于没有显示调用Object#hashcode()方法的类的对象,仍然适用于偏向锁的机制——可被用作锁对象使用。

    HotSpot为所有加载的类型,在class元数据——InstanceKlass中保留了一个MarkWord原型——mark_prototype。这个值的bias位域决定了该类型的对象是否允许被偏向锁定。与此同时,当前的epoch位也被保留在prototype中。这意味着,对应class的新对象可以简单地直接拷贝这个原型值,而不必在后面进行修正。在批量重偏向(bulk rebias)的操作中,prototype的epoch位将会被更新;在批量吊销(bulk revoke)的操作中,prototype将会被置成不可偏向的状态——bias位被置0。

    偏向锁的获取依靠原子CAS指令将线程指针插入MarkWord中。其先决条件是:1.该对象处于匿名偏向状态;2.该对象处于可重偏向状态(一个锁对象仅能被一个线程偏向一次)。只要锁对象被偏向,递归锁定和解锁仅仅需要读取对象头以及对应Klass的prototype去验证偏向是否被吊销。

    HotSpot中偏向锁的撤销是JVM处于在全局安全点时被执行的。在撤销过程中,撤销者会遍历当前偏向线程的锁记录,藉此推断对象当前是否被锁定。如果发现锁对象被一个偏向线程持有,锁记录将被修改——如同轻量级锁被使用一样;如果锁对象未被持有,这是取决于触发撤销的原因,锁对象要么被禁止用作偏向锁,要么被禁止重新偏向于撤销线程。

    即使偏向锁的特性被打开,出于性能(启动时间)的原因在JVM启动后的的头4秒钟这个feature是被禁止的。这也意味着在此期间,prototype MarkWord会将它们的bias位设置为0,以禁止实例化的对象被偏向。4秒钟之后,所有的prototype MarkWord的bias位会被重设为1,如此新的对象就可以被偏向锁定了。

    获取偏向锁的步骤:

    1. 验证对象的bias位
      如果是0,则该对象不可偏向,应该使用轻量级锁算法。
    2. 验证对象所属InstanceKlass的prototype的bias位
      确认prototype的bias为是否被设置。如果没有设置,则该类所有对象全部不允许被偏向锁定;并且该类所有对象的bias位都需要被重置,使用轻量级锁替换。
    3. 校验epoch位
      校验对象的MarkWord的epoch位是否与该对象所属InstanceKlass的prototype的MarkWord的epoch匹配。如果不匹配,则表明偏向已过期,需要重新偏向。这种情况,偏向线程可以简单地使用原子CAS指令重新偏向于这个锁对象。
    4. 校验owner线程
      比较偏向线程ID与当前线程ID。如果匹配,则表明当前线程已经获得了偏向,可以安全返回。如果不匹配,对象锁被假定为匿名偏向状态,当前线程应该尝试使用CAS指令获得偏向。如果失败的话,就尝试撤销(很可能引入安全点),然后回退到轻量级锁;如果成功,当前线程成功获得偏向,可直接返回。
    展开全文
  • # 开启偏向锁 -XX:+UseBiasedLocking # 关闭偏向锁延迟 -XX:BiasedLockingStartupDelay=0 # 查看所有的 JVM 参数 -XX:+PrintFlagsFinal # 设置重偏向阈值 -XX:BiasedLockingBulkRebiasThreshold=20 # 批量重偏向距离...

    锁的验证- 无锁->偏向锁->轻量级锁->重量级锁->无锁过程

    一、JVM 设置参数

    # 开启偏向锁
    -XX:+UseBiasedLocking
    # 关闭偏向锁延迟
    -XX:BiasedLockingStartupDelay=0
    # 查看所有的 JVM 参数
    -XX:+PrintFlagsFinal
    # 设置重偏向阈值
    -XX:BiasedLockingBulkRebiasThreshold=20
    # 批量重偏向距离上次批量重偏向的后重置的延迟时间
    -XX:BiasedLockingDecayTime=25000
    # 设置批量撤销阈值
    -XX:BiasedLockingBulkRevokeThreshold=40
    

    在 JVM 启动的时候会有很多线程在后台运行,例如 GC 线程,Finalizer 线程,VM Thread 线程等,会用到很多同步操作,所以在启动的前 4 秒默认创建的对象都不支持偏向,因为有默认参数

    	-XX:BiasedLockingStartupDelay=4000
    

    二、无锁状态

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4hwbHgh-1594019771902)(/Users/haocongcong/Library/Containers/com.tencent.WeWorkMac/Data/Library/Application Support/WXWork/Data/1688850015369412/Cache/Image/2020-06/无锁二进制.png)]

         public void test1(){
    	     Object model2 = new Object();
            System.out.println(ClassLayout.parseInstance(model2).toPrintable());
            System.out.println("hash: -----" + model2.hashCode());
            // 偏向锁
            System.out.println(ClassLayout.parseInstance(model2).toPrintable());
            binaryToDecimal(model2.hashCode());   
         }
    
    OFFSET  SIZE   TYPE DESCRIPTION            VALUE
          0     4        (object header)       01 00 00 00 (00000001 00000000 00000000 00000000) (1)
          4     4        (object header)       00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)       e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    hash: -----1975358023
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION           VALUE
          0     4        (object header)       01 47 92 bd (00000001 01000111 10010010 10111101) (-1114487039)
          4     4        (object header)       75 00 00 00 (01110101 00000000 00000000 00000000) (117)
          8     4        (object header)       e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    header中前8个字节按照平时习惯的从高位到低位的展示为:

    00000000 00000000 00000000 01110101 10111101 10010010 01000111 00000001

    对照上图。最后3位001,代表无锁状态
    中间31位(01110101 10111101 10010010 01000111)换算成10进制,即为上图打印的hash值:1975358023

    偏向锁

        public static void main(String[] args) throws InterruptedException {
             //JVM 启动 5 秒后创建对象
            Thread.sleep(5000);
            Object object = new Object();
            System.out.println(ClassLayout.parseInstance(object).toPrintable());
            //偏向锁
            synchronized (object){
                System.out.println(ClassLayout.parseInstance(object).toPrintable());
            }
    
        }
    
    //匿名偏向 
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION         VALUE
          0     4        (object header)     05 00 00 00 (00000101 00000000 00000000 00000000) (5)
          4     4        (object header)     00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)     e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    
    //偏向锁
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION        VALUE
          0     4        (object header)    05 68 01 b9 (00000101 01101000 00000001 10111001) (-1191090171)
          4     4        (object header)    f7 7f 00 00 (11110111 01111111 00000000 00000000) (32759)
          8     4        (object header)    e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    

    偏向锁和hashCode方法

    当一个对象已经计算过identity hash code,它就无法进入偏向锁状态;当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销。
    那什么时候对象会计算identity hash code呢?当然是当你调用未覆盖的Object.hashCode()方法或者System.identityHashCode(Object o)时候了。

     public static void main(String[] args) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Object model2 = new Object();
            System.out.println(ClassLayout.parseInstance(model2).toPrintable());
            System.out.println("hash: -----" + model2.hashCode());
    //        System.out.println("hashcode:: "+Integer.toBinaryString(model2.hashCode()));
            System.out.println("----------after hashcode----------");
            System.out.println(ClassLayout.parseInstance(model2).toPrintable());
            synchronized (model2){
                System.out.println("---------after lock-------");
                model2.hashCode();
                System.out.println(ClassLayout.parseInstance(model2).toPrintable());
            }
    
        }
        }
    
    //偏向锁
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    hash: -----1911728085
    ----------after hashcode----------
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           01 d5 a7 f2 (00000001 11010101 10100111 11110010) (-223881983)
          4     4        (object header)                           71 00 00 00 (01110001 00000000 00000000 00000000) (113)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    ---------after lock-------
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           d0 a8 4c 04 (11010000 10101000 01001100 00000100) (72132816)
          4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    轻量级锁

    	public static void main(String[] args) throws InterruptedException {
             //JVM 启动 5 秒后创建对象
            Thread.sleep(5000);
            Object object = new Object();
    
            Thread t1 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 进入偏向锁状态");
                synchronized (object) {
                    System.out.println(ClassLayout.parseInstance(object).toPrintable());
                }
            });
            t1.start();
            t1.join();
    
            new Thread(() -> {
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + " 进入轻量级锁状态");
                    System.out.println(ClassLayout.parseInstance(object).toPrintable());
                }
            }).start();
    
        }
    
    	Thread-0 进入偏向锁状态
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION         VALUE
          0     4        (object header)     05 e8 18 a8 (00000101 11101000 00011000 10101000) (-1474762747)
          4     4        (object header)     a4 7f 00 00 (10100100 01111111 00000000 00000000) (32676)
          8     4        (object header)     e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    Thread-2 进入轻量级锁状态
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION        VALUE
          0     4        (object header)    68 c8 0c 06 (01101000 11001000 00001100 00000110) (101501032)
          4     4        (object header)    00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
          8     4        (object header)    e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    

    #重量级锁

    public static void main(String[] args) throws InterruptedException {
            //JVM 启动 5 秒后创建对象
            Thread.sleep(5000);
            Object object = new Object();
    
            Thread t1 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 进入偏向锁状态");
                synchronized (object) {
    
                    System.out.println(LocalDateTime.now()+",t1锁状态:"+ClassLayout.parseInstance(object).toPrintable());
                }
            });
            t1.setName("t1");
            t1.start();
            t1.join();
    
            Thread t2 = new Thread(() -> {
                synchronized (object) {
                    System.out.println("t2:"+ClassLayout.parseInstance(object).toPrintable());
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t2锁状态:"+ClassLayout.parseInstance(object).toPrintable());
                }
            });
            t2.setName("t2");
            t2.start();
    
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread t3 = new Thread(() -> {
    
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + " 拿到锁,状态:");
                    System.out.println(ClassLayout.parseInstance(object).toPrintable());
                }
            });
            t3.setName("t3");
            t3.start();
    
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("after all:"+ClassLayout.parseInstance(object).toPrintable());
        }
    

    输出结果

    无锁->偏向锁->轻量级锁->重量级锁->无锁

    t1锁状态:java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 40 99 29 (00000101 01000000 10011001 00101001) (697909253)
          4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    t2:java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 40 99 29 (00000101 01000000 10011001 00101001) (697909253)
          4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    t2锁状态:java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           4a 97 81 2a (01001010 10010111 10000001 00101010) (713135946)
          4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    t3 拿到锁,状态:
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           4a 97 81 2a (01001010 10010111 10000001 00101010) (713135946)
          4     4        (object header)                           ef 7f 00 00 (11101111 01111111 00000000 00000000) (32751)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    after all:java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    

    总结一下,其实无锁->偏向锁->轻量级锁->重量级锁的转化过程中没那么复杂,注意记住:
    (1)只有一个线程获取锁时,就是偏向锁。
    (2)多个线程时,不存在竞争(多个线程顺序执行),轻量级锁。
    (3)多个线程存在竞争时重量级锁。
    很多时候,偏向锁->轻量级锁->重量级锁的转化过程,基本你只能看见开头和结尾,因为轻量级锁的自旋被优化不会长时间持续进行的,所以你看到的就是偏向锁到重量级锁的过程。

    展开全文
  • java 偏向锁

    千次阅读 2019-07-23 22:30:52
    文章目录偏向锁1、偏向锁是什么2、优缺点优点缺点3、偏向锁怎么获取前提4、偏向锁的撤销5、可重偏向状态(Rebiasable)6、BiasedLockingBulkRebiasThreshold 参数是干什么用的7、到BiasedLockingBulkRevokeThreshold ...

    偏向锁

    1、偏向锁是什么

    大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。JDK1.6之后 偏向锁默认开启
    偏向锁是锁状态中最乐观的一种锁:从始至终只有一个线程请求同一把锁

    偏向锁对象头的Mark Word:

    线程ID Epoch 对象分代年龄 偏向锁的标志 是否偏向锁 0否 1是 锁标志位
    线程ID(初始为00…代表未偏向) Epoch 对象分代年龄 1 01

    2、优缺点

    优点

    只需要执行一次CAS即可获取锁
    采用延迟释放锁策略
    锁重入时,只需要判断mark_word.threadId(关于对象头的文章)是否为当前threadId即可

    缺点

    总体上只针对第一个线程有效,新线程获取锁时,会导致锁膨胀
    锁膨胀时,会导致stop the world (STW)
    与原生hashcode()互斥,导致偏向锁并非适应于所有的instance

    3、偏向锁怎么获取

    前提

    JVM偏向锁(-XX:+UseBiasedLocking)默认已开启
    确认instance可用偏向锁可用,即mark word 偏向锁的标志为 101

    当一个线程访问同步块并获取锁时,

    会在对象头和栈帧中的锁记录里存储锁偏向的线程ID

    ,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,

    而只需简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁,

    如果测试成功,

    表示线程已经获得了锁,

    如果测试失败,

    则需要再测试下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁)

    ,如果没有设置,

    则使用CAS竞争锁,竞争成果后为轻量级锁

    如果设置了,

    则尝试使用CAS将对象头的偏向锁指向当前线程。

    引用网络上对于获取偏向锁的总结,解释了批量重偏向和批量撤销偏向的原理

    获取偏向锁的步骤

    1、验证对象的bias位

    如果是0,则该对象不可偏向,应该使用轻量级锁算法。

    2、验证对象所属InstanceKlass的prototype的bias位

    确认prototype的bias为是否被设置。如果没有设置,则该类所有对象全部不允许被偏向锁定;并且该类所有对象的bias位都需要被重置,使用轻量级锁替换。

    3、校验epoch位

    校验对象的MarkWord的epoch位是否与该对象所属InstanceKlass的prototype的MarkWord的epoch匹配。如果不匹配,则表明偏向已过期,需要重新偏向。这种情况,偏向线程可以简单地使用原子CAS指令重新偏向于这个锁对象。

    4、校验owner线程

    比较偏向线程ID与当前线程ID。如果匹配,则表明当前线程已经获得了偏向,可以安全返回。如果不匹配,对象锁被假定为匿名偏向状态,当前线程应该尝试使用CAS指令获得偏向。如果失败的话,就尝试撤销(很可能引入安全点),然后回退到轻量级锁;如果成功,当前线程成功获得偏向,可直接返回。

    4、偏向锁的撤销

    偏向锁使用了一种等到竞争出现才释放锁的机制,

    所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。

    偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),

    它会首先暂停拥有偏向锁的线程,

    然后检查持有偏向锁的线程是否活着,

    如果线程不处于活动状态,则将对象头设置成无锁状态,

    如果线程仍然活着,恢复到无锁(标记对象不适合作为偏向锁),最后唤醒暂停的线程。这里路神说的是不会有101变为001但感觉这里修改了偏向锁标识然后竞争轻量级锁。因为无法证明得啃jvm源码了,求同存异

    需要拿锁的线程再去加锁,只能使用轻量级锁了(批量重偏向除外)

    5、可重偏向状态(Rebiasable)

    在此状态下,偏向锁的epoch字段是无效的(与锁对象对应的klass的mark_prototype的epoch值不匹配)。下一个试图获取锁对象的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程。在批量重偏向的操作中,未被持有的锁对象都被至于这个状态,以便允许被快速重偏向。本段摘抄自网络,关于重偏向是否CAS稍后讨论

    6、BiasedLockingBulkRebiasThreshold 参数是干什么用的

    BiasedLockingBulkRebiasThreshold是针对某一Class的实例对象在某一线程偏向锁重偏向的阈值默认是20,
    需要注意。如果没达到这个值发生偏向,请check线程id,两个线程是否一样。
    当当前线程撤销偏向的次数==20以后线程内该类的实例会处于以下状态 总次数应该在InstanceKlass上,没有翻jvm源码,有待验证

    1. 已经经过偏向锁撤销,并使用轻量级锁的对象,状态为001 无锁状态demo1
    2. 当前需要加锁的对象,会check epoch 和偏向锁的标志 重偏向至当前线程 关于重偏向是否CAS逐渐明朗
    3. 后续未加锁未使用的对象 依然是偏向线程1 demo1
    4. 已经使用了轻量级锁的对象不可再次使用偏向锁 demo1
    5. 只要偏向锁在某线程内撤销次数达到此阈值,就修改在class元数据——InstanceKlass中的epoch 是对象和元数据的epoch不一致,而使对象可以重偏向。demo2
    6. 发生过重偏向的对象不可再次重偏向 自己写demo,这里不提供demo了。需要修改到BiasedLockingBulkRevokeThreshold=41 排除此参数的影响

    7、到BiasedLockingBulkRevokeThreshold 参数是干什么用的

    BiasedLockingBulkRevokeThreshold 是批量撤销对象的可偏向状态的判断阈值,由上面引用网络上对于获取偏向锁的总结
    对后面demo2的现象做出了解释未验证,需要求教子路大仙

    1. 偏向锁撤销达到BiasedLockingBulkRevokeThreshold 的阈值(默认40)后,修改对象所属InstanceKlass的prototype的bias(偏向锁的标志是否可偏向)位 使该对象以及该类的所有实例,不再使用偏向锁。所以在默认条件下一个类的对象实例,只可以重偏向一次注意和上面的对象区分开。可修改此阈值。调整重偏向次数,或者提前批量撤销偏向
    2. 偏向锁撤销达到此次数之后,不会影响现有未加锁对象的头(未同步过和已同步过),但这些对象不可使用偏向锁
    3. 偏向锁撤销达到此次数之后由于类模板InstanceKlass的bias(偏向锁的标志是否可偏向)被设置为0,所以new出来的新对象,是无锁状态,不可使用偏向锁。

    8、执行下方demo需要准备的环境

    1. 一个maven项目
    2. 引入依赖
    <dependency>
                <groupId>org.openjdk.jol</groupId>
                <artifactId>jol-cli</artifactId>
                <version>0.9</version>
            </dependency>
    
    1. 需要修改jvm项目的运行参数
     -XX:+PrintFlagsFinal 打印jvm运行时参数,验证参数修改
     -XX:BiasedLockingBulkRebiasThreshold=20 修改批量重偏向阈值
     -XX:BiasedLockingBulkRevokeThreshold=40 修改批量撤销偏向阈值。
    
    

    9、总结和猜想

    1. 校验epoch位时候发现类模板和对象不一致的时候,可以简单的理解为此对象状态约等于 偏向锁未偏向的状态重新进行偏向加锁
    2. 当InstanceKlass类模板的偏向锁标识,标识不可偏向的时候。改类所有实例对象均不可进行偏向加锁
    3. 偏向锁重偏向一次之后不可再次重偏向。
    4. 已经使用过轻量级锁的对象,不可再次偏向,哪怕线程内触发了批量重偏向

    10、参考资料

    1. 轻量级锁、偏向锁、重量级锁详情
    2. 是否真的理解了偏向锁、轻量级锁、重量级锁(锁膨胀)、自旋锁、锁消除、锁粗化,知道重偏向吗?
    3. 偏向锁,轻量级锁,自旋锁,重量级锁的详细介绍
    4. CAS操作、Java对象头、偏向锁的获取与撤销、轻量级锁的获取与撤销、锁粗化、锁消除
    5. Java锁事之偏向锁

    LAST、 demo代码

    //demo1
    
    public class BiasedLockJOLTest {
    
    
        public static void main(String[] args) throws Exception {
            Thread.sleep(6000);
    //        a = new A();
            List<A> list = new ArrayList<>();
            List<B> list2 = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                list.add(new A());
                list2.add(new B());
            }
    
            Thread t1 = new Thread() {
                String name = "1";
    
                public void run() {
                    out.printf(name);
                    for (A a : list) {
                        synchronized (a) {
                            if (a == list.get(10))
                                out.println("t1 预期是偏向锁"+10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁
    
                        }
    
                    }
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            };
            t1.start();
            Thread.sleep(5000);
            out.println("main 预期是偏向锁 同步结束后不撤销偏向锁,在下次使用的时候进行撤销偏向并膨胀为轻量级锁或者重锁 或重偏向"+10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁
    
            Thread t2 = new Thread() {
                String name = "2";
    
                public void run() {
                    out.printf(name);
    
                    for(int i = 0;i<100;i++){
                        A a = list.get(i);
                        if(i==20){
                            a= list.get(9);
                        }
                        synchronized (a) {
                            if ( a == list.get(10)) {
                                out.println("t2 i=10 get(1)预期是无锁" +  ClassLayout.parseInstance(list.get(1)).toPrintable());//偏向锁
                                out.println("t2 i=10 get(10) 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                            }
                            if ( a == list.get(19)) {
                                out.println("t2  i=19  get(10)预期是无锁" + 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//无锁 不可偏向
                                out.println("t2  i=19  get(19) 满足重偏向条件20 预期偏向锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
    
                                out.println("t2  i=19  get(40) 未同步到的对象,依然偏向t1 " + i + ClassLayout.parseInstance(list.get(40)).toPrintable());//偏向锁
                            }
                            if (i == 20) {
                                out.println("t2  i=20  get(9)预期是轻量级锁,因为无锁 不可偏向的标识不可重新更改为可偏向状态,所以再次锁定之前的对象依然使用了轻量级锁" + 10 + ClassLayout.parseInstance(a).toPrintable());//轻量级锁
    
    
                            }
    
                        }
    
    
    
                    }
    
    
    
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
    
                }
            };
            t2.start();
            }
        }
    
    //demo2
    
    public class BiasedLockJOLTest2 {
        static A a;
    
        public static void main(String[] args) throws Exception {
            Thread.sleep(6000);
    //        a = new A();
            List<A> list = new ArrayList<>();
            List<A> list2 = new ArrayList<>();
            List<A> list3 = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                list.add(new A());
                list2.add(new A());
                list3.add(new A());
            }
            out.println("初始状态" + 10 + ClassLayout.parseClass(A.class).toPrintable());//偏向锁
    
            Thread t1 = new Thread() {
                String name = "1";
    
                public void run() {
                    out.printf(name);
                    for (A a : list) {
                        synchronized (a) {
                            if (a == list.get(10))
                                out.println("t1 预期是偏向锁" + 10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                        }
                    }
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            };
            t1.start();
            Thread.sleep(5000);
            out.println("main 预期是偏向锁" + 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁
    
            Thread t2 = new Thread() {
                String name = "2";
    
                public void run() {
                    out.printf(name);
    
                    for (int i = 0; i < 100; i++) {
                        A a = list.get(i);
                        synchronized (a) {
                            if (a == list.get(10)) {
                                out.println("t2 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list.get(1)).toPrintable());//偏向锁
                                out.println("t2 i=10 get(10) 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                            }
                            if (a == list.get(19)) {
                                out.println("t2  i=19  get(10)预期是无锁" + 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁
                                out.println("t2  i=19  get(19) 满足重偏向条件20 预期偏向锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                                out.println("A类的对象累计撤销达到20");
                            }
    
                        }
                    }
    
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
    
                }
            };
            t2.start();
    
            Thread.sleep(5000);
    
    
            Thread t3 = new Thread() {
                String name = "3";
    
                public void run() {
    
                    out.printf(name);
                    for (A a : list2) {
                        synchronized (a) {
                            if (a == list2.get(10))
                                out.println("t3 预期是偏向锁" + 10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                        }
                    }
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t3.start();
            Thread.sleep(5000);
    
    
            Thread t4 = new Thread() {
                String name = "4";
    
                public void run() {
    
                    out.printf(name);
    
                    for (int i = 0; i < 100; i++) {
                        A a = list2.get(i);
                        synchronized (a) {
                            if (a == list2.get(10)) {
                                out.println("t4 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list2.get(1)).toPrintable());//偏向锁
                                out.println("t4 i=10 get(10) 当前不满足重偏向条件 20 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                            }
                            if (a == list2.get(19)) {
                                out.println("t4  i=19  get(10)预期是无锁" + 10 + ClassLayout.parseInstance(list2.get(10)).toPrintable());//偏向锁
                                out.println("t4 i=19 get(19) 当前满足重偏向条件 20 但A类的对象累计撤销达到40 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                                out.println("A类的对象累计撤销达到40");
                            }
                            if (a == list2.get(20)) {
                                out.println("t4 i=20 get(20) 当前满足重偏向条件 20 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
    
                            }
                        }
                    }
    
                }
            };
            t4.start();
            Thread.sleep(5000);
    
    
            out.println("main 预期是偏向锁" + 10 + ClassLayout.parseInstance(list3.get(0)).toPrintable());//偏向锁
    
            Thread t5 = new Thread() {
                String name = "5";
    
                public void run() {
                    out.printf(name);
                    for (A a : list3) {
                        synchronized (a) {
                            if (a == list3.get(10))
                                out.println("t5 预期是轻量级锁,A类的对象累计撤销达到40 不可以用偏向锁了" + 10 + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                        }
                    }
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
            };
            t5.start();
            Thread.sleep(5000);
            out.println("main 预期是偏向锁" + 10 + ClassLayout.parseInstance(list.get(10)).toPrintable());//偏向锁
    
            Thread t6 = new Thread() {
                String name = "6";
    
                public void run() {
                    out.printf(name);
    
                    for (int i = 0; i < 100; i++) {
                        A a = list3.get(i);
                        synchronized (a) {
                            if (a == list3.get(10)) {
                                out.println("t6 i=10 get(1)预期是无锁" + ClassLayout.parseInstance(list3.get(1)).toPrintable());//偏向锁
                                out.println("t6 i=10 get(10) 预期轻量级锁 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
                            }
                            if (a == list3.get(19)) {
                                out.println("t6  i=19  get(10)预期是无锁" + 10 + ClassLayout.parseInstance(list3.get(10)).toPrintable());//偏向锁
                                out.println("t6  i=19  get(19) 满足重偏向条件20 但A类的对象累计撤销达到40 不可以用偏向锁了 " + i + ClassLayout.parseInstance(a).toPrintable());//偏向锁
    
                            }
    
                        }
                    }
    
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
    
                }
            };
            t6.start();
    
            Thread.sleep(5000);
    
    
            out.println("由于A撤销锁次数达到默认的 BiasedLockingBulkRevokeThreshold=40 这里实例化的对象 是无锁状态" + ClassLayout.parseInstance(new A()).toPrintable());//偏向锁
            out.println("由于B撤销锁次数没达到默认的 BiasedLockingBulkRevokeThreshold=40 这里实例化的对象 是偏向锁可以偏向状态 理论上可以再次验证上面A类的相关操作" + ClassLayout.parseInstance(new B()).toPrintable());//偏向锁
            out.println("撤销偏向后状态" + 10 + ClassLayout.parseClass(A.class).toPrintable());//偏向锁
    
        }
    
    
    }
    
    
    展开全文
  • 文章目录Java锁synchronized关键字学习系列之偏向锁升级无锁偏向锁原理批量重偏向和批量撤销偏向锁升级偏向锁升级轻量级锁偏向锁升级重量级锁参考源代码 Java锁synchronized关键字学习系列之偏向锁升级 前面几篇博文...
  • 轻量级锁、偏向锁、重量级锁详情

    千次阅读 2018-11-17 13:48:47
    这篇文章是上篇文章是否真的理解了偏向锁、轻量级锁、重量级锁(锁膨胀)、自旋锁、锁消除、锁粗化,知道重偏向吗?的补充,对于偏向锁,网上有些对于它的原理解读过于简单,简单得似乎是错误的,最显眼的是对于Mark...
  • 轻量锁 偏向锁

    千次阅读 2015-07-19 18:36:16
    偏向锁  http://blog.163.com/silver9886@126/blog/static/35971862201472274958280/ Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行...
  • 由于只有一个线程加锁,所以预期是加偏向锁 public class Test { public static void main(String[] args) throws InterruptedException { //TimeUnit.SECONDS.sleep(5); 代码很重要,先注释掉,后面需要放开 ...
  • 文章目录Java锁Synchronized关键字学习系列之偏向锁synchronized 锁升级偏向锁匿名偏向偏向锁的加锁和释放锁偏向锁流程图总结学习参考源代码 Java锁Synchronized关键字学习系列之偏向锁 synchronized 锁升级 在多...
  • 锁的分类 为了减少获得锁和释放锁带来的性能消耗,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁...如果不是则去验证当前对象头是否标记为偏向锁如果不是就cas竞争,如果是就
  • 结论: 开启偏向锁,默认4s后启动偏向锁,但是睡眠了5s后,就直接轻量级锁了,是说明必须4s后创建的对象才有偏向锁?下面验证 结论: 4s后创建的对象存在匿名偏向和获取偏向锁的行为
  • 偏向锁的理解

    千次阅读 2019-05-04 19:28:03
    偏向锁: 是为了在资源没有被其他线程竞争的情况下尽量减少锁带来的性能开销。在锁对象的对象头中有一个ThreadId字段,当第一个线程访问锁时,如果该锁没有被其他线程访问,即ThreadId字段为空,那么JVM让其持有...
  • Java--偏向锁/轻量级锁/重量级锁

    千次阅读 2018-04-10 14:34:01
    另外偏向锁---轻量级锁---重量级锁本文都需要依托synchronize进行理解和分析。另外也要参照网络上很多的资料。1.对象头:关于对象头的具体记录,可以参考这边:对象头2.同步的原理:关于JVM规范对于同步的解释,可以...
  • Java中的偏向锁,轻量级锁, 重量级锁解析

    万次阅读 多人点赞 2018-08-13 18:39:49
    参考文章 聊聊并发(二)Java SE1.6中的Synchronized Lock Lock Lock: Enter! 5 Things You Didn’t Know About Synchronization in Java and Scala ...Java 中的 在 Java 中主要2种加锁机制: synchr...
  • 禁止延迟开启偏向锁jvm参数

    千次阅读 热门讨论 2020-03-14 20:51:13
    jdk6默认开启偏向锁,但是是输入延时开启,也就是说,程序刚启动创建的对象是不会开启偏向锁的,几秒后后创建的对象才会开启偏向锁的 -XX:BiasedLockingStartupDelay=0 验证 引入依赖包 <!--可以查看mark word...
  • Java中的偏向锁是什么

    2020-10-30 10:06:11
    但是你知道如何去验证偏向锁呢?有相关的源码或者代码给你看过吗? 那么这篇文章只是为了证明偏向锁是否真的存在,它为什么要比重量级锁快? 在有了上篇文章的知识铺垫后知道每当java线程创建的时候相对应的操作系统...
  • Java对象头Object Header。包括markword、klasspoint。 里面有hashcode、gc 的age,还有锁状态,包括:偏向锁、轻量锁、重量锁研究
  • Java 偏向锁测试

    2016-01-27 22:44:17
    //可以明显的看到使用偏向锁,单线程时间减少,但是多线程时间延长 pool-1-thread-1spend 563 ms +UseBiasedLocking : 563 ms pool-2-thread-4spend 2121 ms pool-2-thread-2spend 2130 ms pool-2-...
  • } /* 未加偏向锁,虚拟机在启动的时候对于偏向锁有延迟,延迟是4000ms,所以该段代码没有加上偏向锁 */ public static void methodA() throws Exception{ ObjectA objectA = new ObjectA(); System.out.println(...
  • Mark Word:存储对象的hashcode、分代年龄、信息等 Class Metadata Address(类型指针):存储到对象类型数据的指针 Array length:数组长度(数组特有) 实例数据 对象真正存储的有效信息,继承自父类及子类所...
  • 乐观是对于数据冲突保持一种乐观态度,操作数据时不会对数据锁定(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般是通过加版本号然后进行比对的方式...
  • MarkWord记录对象的状态、分代年龄等对象信息。以下为64bitJVM的对象头MarkWord信息: 具体关于对象内存布局信息的知识,可以参见:Java对象的内存布局 本文主要介绍如何使用JOL去观察 二、使用Maven导入JOL相关...
  • 回到我们今天的主题:证明JDK1.6以后,JDK对synchronized关键字进行了优化,引入的偏向锁(当线程没有竞争的时候,偏向锁只会加锁一次,后面再去调用该方法的时候,不会再去操作系统调用对应的操作的系统的加锁的...
  • synchronized前言JDK对Synchronize的优化-锁膨胀偏向锁轻量级锁重量级锁重量级锁模式下的SynchronizeSynchronize 的核心组件Synchronized 的流程Synchronized 的实现Synchronized 的作用范围synchronized的简单使用 ...
  • 1.三种的简介 内置是JVM提供的最便捷的线程同步工具,在代码块或方法声明上添加synchronized关键字即可使用内置。对其优化使的juc包中的各种类性能提高。 重量级:内置在Java中被抽象为监视器(monitor...
  • 在介绍之前我们先介绍一个线程不安全的例子,一个全局的list,开2个线程往里面插入数据,代码如下:   package com.jvm.day6.lock.demo; import java.util.ArrayList; import java.util.List; /** * ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,633
精华内容 2,253
关键字:

偏向锁验证