多线程四 并发中锁的原理

目录

1.打印jvm信息

2. mark word分析

hashCode

无锁(001)

轻量级锁(000)

偏向锁(101)

加锁与解锁的过程

重量级锁(010)

总结



先来引入锁的概念:

偏向锁:当前只有一个锁,无线程竞争的情况下,尽量减少不必要的轻量锁的执行路径。

偏向锁就是在运行过程中,对象的锁偏向某个线程,即在开启偏向锁的情况下,某个线程获得锁,当该线程下次想要获得锁时,不需要再获取锁(忽略synchronized关键字),直接执行代码

轻量锁:存在锁之间的竞争,但竞争的会很小,

重量锁:存在资源竞争


引入java对象布局工具,用于查看对象信息

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

1.打印jvm信息

public class TestA {
    private boolean a;
    private int b;
}
public class TestSyncWord {
    public static void main(String[] args) {
        TestA a =  new TestA();
        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(TestA.class).toPrintable(a));
    }
}

得到以下结果:

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
// 对应:[Oop(Ordinary Object Pointer), boolean, byte, char, short, int, float, long, double]大小
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

com.lry.thread.TestA 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

从上面结果可以看出:

  • 对象头(object header)占3*4=12 B

  • 实例对象数据(TestA.b 和 TestA.a)占4+1=5B

  • 对齐占字节7B

整个对象占24B

先看一下对象头的定义:

对象头里面的专业术语查看:

HotSpot Glossary of Terms

openjdk对对象头的注释

//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
  未使用:25位  hashcode:31位  未使用:1位  分代年龄:4位  偏向标志位:1位   对象状态:2位
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)

由此可以看出一个对象头的组成

> 未使用:25位  hashcode:31位  未使用:1位  分代年龄:4位  偏向标志位:1位   对象状态:2位

  java对象头在对象的不同状态下会有不同的表现形式,主要由:无所状态、加锁状态、gc标记状态。那么我们可以理解java当中的取锁其实可以理解是给对象上锁,也就是改变对象头的状态,如果上锁成功则进入同步代码块,但是java当代中的锁又分很多种,从上图可以看出大体分为偏向锁、轻量锁、重量锁三种锁状态。这三种锁的效率完全不同、关于效率的分析会在下文分析。

     一个对象头有mark word 和klass pointer两个部分组成(数组对象除外,数组对象的对象头还包含一个数组长度),那么一个java的对象头有多大呢?

从源码注释中可以知道一个对象布局信息是64bit(虚拟机是64位),从上面代码解析的结果来看,对象头是12B,对象头又包含:mark word和klass pointer,mark word固定为8B(64位虚拟机是8B,32位是4B),那么klass pointer(类指针)=4B;

2. mark word分析

接下来重点分析下mark word的信息;

在无锁的情况下mark word当中的前56bit存的是对象的hashcode(在对象头信息中,前56位有25位未使用,实际是31位);

hashCode

如下程序,如果单单是打印信息是看不到hashCode迹象的,因为hashCode需要计算后才能写入,所以下面才会调用hashCode方法。

public static void main(String[] args) {
    TestA a =  new TestA();
    System.out.println("计算前的hashcode");
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
    // 计算hashcode
    System.out.println("16进制的hashcode:"+Integer.toHexString(a.hashCode()));
    System.out.println("计算后的hashcode");
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
计算前的hashcode
com.lry.thread.TestA 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total


16进制的hashcode:5fdef03a
计算后的hashcode
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 3a f0 de (00000001 00111010 11110000 11011110) (-554681855)
      4     4           (object header)                           5f 00 00 00 (01011111 00000000 00000000 00000000) (95)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

要看懂上面的信息,需要引入一个概念:小端对齐。

那什么是小端对齐呢?

比如一个整数有个十百千位,那么小端对齐就是以这个顺序进行排列,左边的就是低位;

所以上面的值要反过来看:

0   4     (object header)       01 3a f0 de (00000001 00111010 11110000 11011110) (-554681855)
4   4     (object header)       5f 00 00 00 (01011111 00000000 00000000 00000000) (95)

开头的00000001不包含在hashCode的表示中,剩下:00 00 00 5f de f0 3a 前3字节,24位,没有使用。那5f de f0 3a就是他的hashcode,也和我们打印的hashcode一样,但对象头中记录hash只有31位,那么这里面有1位不在hashCode计算内。

无锁(001)

关于最后那个没有使用的字节(00000001)是对锁关系的表示:

0                 0000       0         01

没有使用   分代年龄   偏向  对象状态

对象状态一共有五种,分别是:无锁、偏向锁、轻量锁、重量锁、GC标识。

到这里,打印的对象信息可以解析为:

从上一个例子(只是单纯的new出来,没有锁的情况)中可以看出,无锁状态(001);

轻量级锁(000)

这时,如果对这个对象使用synchronized(加锁)后,它的对象头会产生变化:

public static void main(String[] args) {
    TestA a = new TestA();
    synchronized (a) {
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           98 f0 a3 02 (10011000 11110000 10100011 00000010) (44298392)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)

结果不是偏向锁,因为偏向标志位0,那么就是轻量级锁(000)

偏向锁(101)

如果:

public static void main(String[] args) {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    TestA a = new TestA();
    synchronized (a) {
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           05 68 15 03 (00000101 01101000 00010101 00000011) (51734533)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

偏向标志位位1,为偏向锁,这什么情况?

一般代码中的程序都是偏向锁,因为一般程序执行都是在一个线程下执行,并不会涉及到多线程争抢锁的情况,所以jvm把所有程序都进行了偏向,所有jvm在启动时对偏向锁延迟了。

所以上面代码中会出现sleep,还可以使用下面参数来设置这个值:

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

jvm也提供了其他的一些参数,我们可以使用:-XX:+PrintFlagsFinal打印jvm的一些设置信息,可以看到延迟偏向的时间是4000,当然这个值也不是准确值,他只是延迟到这个时间去触发,执行的效率我们也不知道。

加锁与解锁的过程

public class TestA {

    private boolean a;

    private int b;

    public synchronized void count(){
        b++;
    }

    public void count2(){
        b++;
    }
}
public static void main(String[] args) {
    TestA a = new TestA();
    System.out.println("轻量级锁 start。。。");
    System.out.println(ClassLayout.parseInstance(a).toPrintable());

    synchronized (a) {
        a.count2();
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
    System.out.println("end ...");
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
轻量级锁 start。。。
com.lry.thread.TestA 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           a8 f0 5a 03 (10101000 11110000 01011010 00000011) (56291496)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   1
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

end ...
com.lry.thread.TestA 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   1
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

资源加锁后,升级为轻量级锁,然后在释放锁后,变为无锁状态。

重量级锁(010)

 public static void main(String[] args) {
        TestA a = new TestA();

        // 这里是无锁001
        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        Thread t = new Thread(){
            @Override
            public void run() {
                synchronized (a) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    a.count2();
                    System.out.println("sync  ing--------");
                }
            }
        };

        t.start();
        System.out.println("执行第一个线程(轻量级锁)---------");
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
        // 去除线程复用
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("睡眠1秒后(轻量级锁)--------");
        // 轻量锁 000;因为没设置偏向锁的延迟时间
        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        synchronized (a) {
            System.out.println("主线程加锁(重量级锁)--------");
            // 存在资源竞争,膨胀为重量级锁 010
            System.out.println(ClassLayout.parseInstance(a).toPrintable());
        }
        System.out.println("主线程解锁后(状态为改变)--------");
        // 在解锁后,标志未改变
//        System.out.println(a.hashCode());
        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        System.gc();;
        System.out.println("GC 方法启动--------");
        // 资源回收,无锁 001

        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
com.lry.thread.TestA 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)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

执行第一个线程(轻量级锁)---------
com.lry.thread.TestA object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           08 f3 0a 21 (00001000 11110011 00001010 00100001) (554365704)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

睡眠1秒后(轻量级锁)--------
com.lry.thread.TestA object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           08 f3 0a 21 (00001000 11110011 00001010 00100001) (554365704)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

sync  ing--------
主线程加锁(重量级锁)--------
com.lry.thread.TestA object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           0a 19 2e 1d (00001010 00011001 00101110 00011101) (489560330)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   1
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

主线程解锁后(状态为改变)--------
com.lry.thread.TestA object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           0a 19 2e 1d (00001010 00011001 00101110 00011101) (489560330)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   1
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

GC 方法启动--------
com.lry.thread.TestA object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
     12     4       int TestA.b                                   1
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

很清晰的看到:

当jvm启动后,因为短时几秒的延迟偏向,一开始是无锁状态;

执行第一个线程加锁(睡眠5秒),变为轻量级锁;

睡眠1秒后(避免线程复用);

主线程加锁(拿不到锁),变为重量级锁;

GC启动后,回收对象,清除状态;

如果,我们在启动时加上-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0(将延迟偏向时间改为0),那么一开始的打印结果就不会是无锁,而是偏向锁:101,

之后,主线程加锁时,还是拿不到锁,就会升级为重量级锁,跳过了轻量级锁。

注意:当我们调用wait方法后,偏向锁就会立即变为重量级锁。

我们修改代码:让加锁对象阻塞

public static void main(String[] args) {
    TestA a = new TestA();
    System.out.println("进入线程前。。。");
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
    Thread t = new Thread(()->{
        synchronized (a) {
            System.out.println("进入 线程 方法内。。");
            System.out.println(ClassLayout.parseInstance(a).toPrintable());

            try {
                a.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程等待结束。。。");
            System.out.println(ClassLayout.parseInstance(a).toPrintable());


        }
    });

    t.start();

    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("等待4秒后,主线程执行。。。");
    synchronized (a) {
        a.notifyAll();
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}
进入线程前。。。
com.lry.thread.TestA 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

进入 线程 方法内。。
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           05 68 ad 20 (00000101 01101000 10101101 00100000) (548235269)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

等待4秒后,主线程执行。。。
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           8a 5d 4f 1d (10001010 01011101 01001111 00011101) (491740554)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

线程等待结束。。。
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           8a 5d 4f 1d (10001010 01011101 01001111 00011101) (491740554)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4       int TestA.b                                   0
     16     1   boolean TestA.a                                   false
     17     7           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total

刚开始都是偏向锁,但有一点不同,就时第一行(对象头信息),状态后面的值不同,第一个结果是0 ,第二个的结果是非0,可以理解为第一个0的是没有线程持有,而第二个加锁后,有线程持有,偏向于加锁的这个线程。在hashcode运算之前,这种可变的状态称为可偏向状态。

注意:还有重要的一点时,当计算过hashcode后,就不能偏向了

public static void main(String[] args) {
    TestA a = new TestA();
    a.hashCode();
    synchronized (a) {
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

 没有计算前

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)

 计算后的

OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 c2 54 9e (00000001 11000010 01010100 10011110) (-1638612479)
      4     4           (object header)                           0e 00 00 00 (00001110 00000000 00000000 00000000) (14)

轻量级锁执行后就变为了无锁。

偏向锁和轻量锁性能对比

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

public static void main(String[] args) {
    TestA a = new TestA();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000000L; i++) {
        synchronized (a) {
            a.count2();
        }
    }
    long endd = System.currentTimeMillis();
    System.out.println("用时:"+(endd - start));
}

轻量级锁和重量级锁对比

public static void main(String[] args) throws InterruptedException {
    TestA a = new TestA();
    long start = System.currentTimeMillis();
    CountDownLatch latch = new CountDownLatch(10000000);
    for (int i = 0; i < 2; i++) {
        new Thread(()->{
            while (latch.getCount() > 0) {
                synchronized (a) {
                    a.count2();
                    latch.countDown();
                }
            }
        }).start();
    }

    latch.await();

    long end = System.currentTimeMillis();
    System.out.println("用时:"+(end-start));
}

 得到下面的数据,偏向锁的性能,可见他们的差距非常大。

偏向

轻量

重量

16

250

377

总结

  1. 对象由:对象头、实例对象数据、对齐字节组成;
  2. 对象头占64位(32位虚拟机是32位),但只有31位表示hashcode,对象头在windows上的表示要倒着看,然后最后一位表示对象的状态;
  3. 加锁就是改变对象头中的标志位
    1. 无锁 001
    2. 偏向锁  101
    3. 轻量级锁 000
    4. 重量级锁 010
  4. 计算过hashcode后,就不能再偏向了(不会有偏向锁);
  5. 使用了wait方法,锁会立即变为重量级锁;
  6. 如果一直获取不到锁,就会升级重量级锁

  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值