zoukankan      html  css  js  c++  java
  • 偏向锁

    轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有。

    image-20210128200802772

    偏向状态

    回忆一下对象头格式

    image-20210128200834714

    一个对象创建时:

    • 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的thread、epoch、age 都为 0
    • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟
    • 如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,第一次用到 hashcode 时才会赋值
    测试偏向锁
    /**
     * @author WGR
     * @create 2021/1/28 -- 15:20
     */
    @Slf4j(topic = "c.TestBiased")
    public class TestBiased {
    
        public static void main(String[] args) {
            Dog d = new Dog();
            log.debug( ClassLayout.parseInstance(d).toPrintable());
            sleep(3);
            log.debug( ClassLayout.parseInstance(new Dog()).toPrintable());
        }
    }
    
    class Dog{
    
    }
    

    image-20210128153437717

    因为偏向锁有几秒的延迟。-XX:BiasedLockingStartupDelay=0

    image-20210128153842578

    当取消延迟后发现,2个锁标准都是101,变成了偏向锁。

    @Slf4j(topic = "c.TestBiased")
    public class TestBiased {
    
        public static void main(String[] args) {
            Dog d = new Dog();
            log.debug( ClassLayout.parseInstance(d).toPrintable());
            synchronized (d){
                log.debug( ClassLayout.parseInstance(d).toPrintable());
            }
            log.debug( ClassLayout.parseInstance(d).toPrintable());
        }
    }
    
    class Dog{
    
    }
    

    image-20210128153907672

    注意:处于偏向锁的对象解锁后,线程 id 仍存储于对象头中

    测试禁用

    在上面测试代码运行时在添加 VM 参数 -XX:-UseBiasedLocking 禁用偏向锁

    image-20210128154237202

    禁用后直接是轻量级锁。

    撤销 -HashCode

    调用了对象的 hashCode,但偏向锁的对象 MarkWord 中存储的是线程 id,如果调用 hashCode 会导致偏向锁被撤销
    轻量级锁会在锁记录中记录 hashCode,重量级锁会在 Monitor 中记录 hashCode.

    /**
     * @author WGR
     * @create 2021/1/28 -- 15:20
     */
    @Slf4j(topic = "c.TestBiased")
    public class TestBiased {
    
        public static void main(String[] args) {
    
    
            Dog d = new Dog();
    
            d.hashCode();
            log.debug( ClassLayout.parseInstance(d).toPrintable());
            synchronized (d){
                log.debug( ClassLayout.parseInstance(d).toPrintable());
            }
            log.debug( ClassLayout.parseInstance(d).toPrintable());
    
        }
    }
    
    class Dog{
    
    }
    

    image-20210128204559553

    撤销 - 其它线程使用对象

    当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

    /**
     * @author WGR
     * @create 2021/1/28 -- 15:20
     */
    @Slf4j(topic = "c.TestBiased")
    public class TestBiased {
    
        public static void main(String[] args) {
    
    
            Dog d = new Dog();
    
            new Thread(() ->{
                log.debug( ClassLayout.parseInstance(d).toPrintable());
                synchronized (d){
                    log.debug( ClassLayout.parseInstance(d).toPrintable());
                }
                log.debug( ClassLayout.parseInstance(d).toPrintable());
                synchronized (TestBiased.class) {
                    TestBiased.class.notify();
                }
            },"t1").start();
    
            new Thread(() ->{
                synchronized (TestBiased.class){
                    try {
                        TestBiased.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                log.debug( ClassLayout.parseInstance(d).toPrintable());
                synchronized (d){
                    log.debug( ClassLayout.parseInstance(d).toPrintable());
                }
                log.debug( ClassLayout.parseInstance(d).toPrintable());
            },"t2").start();
    
        }
    }
    
    class Dog{
    
    }
    

    结果

    16:08:42.918 c.TestBiased [t1] - com.dalianpai.thread.Dog 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)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    16:08:42.921 c.TestBiased [t1] - com.dalianpai.thread.Dog object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 b0 9f 1e (00000101 10110000 10011111 00011110) (513781765)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    16:08:42.922 c.TestBiased [t1] - com.dalianpai.thread.Dog object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 b0 9f 1e (00000101 10110000 10011111 00011110) (513781765)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    16:08:42.923 c.TestBiased [t2] - com.dalianpai.thread.Dog object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           05 b0 9f 1e (00000101 10110000 10011111 00011110) (513781765)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    16:08:42.924 c.TestBiased [t2] - com.dalianpai.thread.Dog object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           80 f2 1d 1f (10000000 11110010 00011101 00011111) (522056320)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    16:08:42.925 c.TestBiased [t2] - com.dalianpai.thread.Dog 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)                           a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    
    批量重偏向

    如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得,我是不是偏向错了呢,于是会在给这些对象加锁时重新偏向至加锁线程

    private static void test3() throws InterruptedException {
    Vector<Dog> list = new Vector<>();
    Thread t1 = new Thread(() -> {
    for (int i = 0; i < 30; i++) {
    Dog d = new Dog();
    list.add(d);
    synchronized (d) {
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }
    synchronized (list) {
    list.notify();
    }
    }, "t1");
    t1.start();
    Thread t2 = new Thread(() -> {
    synchronized (list) {
    try {
    list.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    log.debug("===============> ");
    for (int i = 0; i < 30; i++) {
    Dog d = list.get(i);
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }, "t2");
    t2.start();
    }
    
    批量撤销

    当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

    static Thread t1,t2,t3;
    private static void test4() throws InterruptedException {
    Vector<Dog> list = new Vector<>();
    int loopNumber = 39;
    t1 = new Thread(() -> {
    for (int i = 0; i < loopNumber; i++) {
    Dog d = new Dog();
    list.add(d);
    synchronized (d) {
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }
    LockSupport.unpark(t2);
    }, "t1");
    t1.start();
    t2 = new Thread(() -> {
    LockSupport.park();
    log.debug("===============> ");
    for (int i = 0; i < loopNumber; i++) {
    Dog d = list.get(i);
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    LockSupport.unpark(t3);
    }, "t2");
        t2.start();
    t3 = new Thread(() -> {
    LockSupport.park();
    log.debug("===============> ");
    for (int i = 0; i < loopNumber; i++) {
    Dog d = list.get(i);
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    log.debug(i + "	" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }, "t3");
    t3.start();
    t3.join();
    log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
    }
    
  • 相关阅读:
    TreeSet和TreeMap中“相等”元素可能并不相等
    求众数——摩尔投票
    5802. 统计好数字的数目
    快速幂
    LCP 07.传递消息
    332. 重新安排行程(欧拉回路问题)
    126. 单词接龙 II
    879. 盈利计划
    287. 寻找重复数
    239. 滑动窗口最大值
  • 原文地址:https://www.cnblogs.com/jinronga/p/14393198.html
Copyright © 2011-2022 走看看