前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程四 并发中锁的原理

多线程四 并发中锁的原理

作者头像
用针戳左手中指指头
发布2021-01-29 11:06:46
5660
发布2021-01-29 11:06:46
举报
文章被收录于专栏:学习计划学习计划

先来引入锁的概念:

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

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

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

重量锁:存在资源竞争


openjdk对对象头的注释

代码语言:javascript
复制
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  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)

一个对象包括:

  • 对象头 ->mark word(64bit) + klass word (64bit)
  • 对象属性
  • 对齐 字节

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

引入java对象布局工具

代码语言:javascript
复制
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

1.打印jvm信息

代码语言:javascript
复制
public class TestA {
    private boolean a;
    private int b;
}
代码语言:javascript
复制
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));
    }
}

得到以下结果:

代码语言:javascript
复制
# 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

从上面结果可以看出:

  • 对象头占3*4=12 B
  • 实例对象数据占4+1=5B
  • 对齐占字节7B

整个对象占24B

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

http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html

Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format. 每个GC管理的堆对象开头的通用结构。 (每个oop都指向一个对象标头。)包括有关堆对象的布局,类型,GC状态,同步状态和标识哈希码的基本信息。 由两个词组成。 在数组中,紧随其后的是长度字段。 请注意,Java对象和VM内部对象都具有通用的对象标头格式。 klass pointer The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable". 每个对象标头的第二个字。 指向另一个对象(元对象),该对象描述原始对象的布局和行为。 对于Java对象,“容器”包含C ++样式“ vtable”。 mark word The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits. 每个对象标头的第一个单词。 通常,一组位域包括同步状态和标识哈希码。 也可以是指向与同步相关的信息的指针(具有特征性的低位编码)。 在GC期间,可能包含GC状态位。

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

从源码注释中可以知道一个mark word 是64bit,从上面代码解析的结果来看,对象头是12B,mark word固定为8B,那么klass pointer=4B;

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

在无锁的情况下mark word当中的前56bit存的是对象的hashcode,但是我们的结果,很明显的看不出想hashcode的迹象,是因为还没有计算,

代码语言:javascript
复制
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());
}
代码语言:javascript
复制
计算前的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

这里引入一个概念:小端对齐,那什么是小端对齐呢?

高内存地址放整数的高位,低内存地址放整数的低位,就叫小端对齐,Windows都是这样的。

从上面计算hashcode后的打印可以看到它的对象头发生变化;上面对象头中说有25位没有使用,31为是hashcode,剩下的对象的状态表示。

代码语言:javascript
复制
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,剩下:00 00 00 5f de f0 3a 前3位,24位,没有使用。那5f de f0 3a就是他的hashcode,也和我们打印的hashcode一样。

那最后那个没有使用的字节是怎样的表示呢?

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

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

从上一个例子中可以看出,无锁状态表示是001

代码语言:javascript
复制
public static void main(String[] args) {
    TestA a = new TestA();
    synchronized (a) {
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}
代码语言:javascript
复制
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)

如果:

代码语言:javascript
复制
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());
    }
}
代码语言:javascript
复制
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在启动时对偏向锁延迟了,在启动后再加上锁,所有才会出现上面代码sleep,还可以使用下面参数来设置这个值。

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

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

加锁于解锁的过程

代码语言:javascript
复制
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());
}
代码语言:javascript
复制
轻量级锁 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

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

重量级锁的情况:

代码语言:javascript
复制
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("线程t 启动后,主线程睡眠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(ClassLayout.parseInstance(a).toPrintable());

    System.gc();;
    System.out.println("GC 方法启动--------");
    // 资源回收,无锁 001
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
代码语言:javascript
复制
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)                           d8 ef 4e 20 (11011000 11101111 01001110 00100000) (542044120)
      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


线程t 启动后,主线程睡眠1秒--------
com.lry.thread.TestA object internals:
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           d8 ef 4e 20 (11011000 11101111 01001110 00100000) (542044120)
      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)                           ea 12 79 1c (11101010 00010010 01111001 00011100) (477696746)
      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)                           ea 12 79 1c (11101010 00010010 01111001 00011100) (477696746)
      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秒后,主线程取,发现现在资源被线程t加了锁,这时对象状态还未改变,当主线程sync调用a.方法后,发现a对象资源正在被线程t加锁中,所以这时候就出现资源竞争,变为重量级锁。

如果,我们在启动时加上-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0,那么一开始的打印结果就不会是无锁,而是偏向锁:101, 结果就不贴上了。

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

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

代码语言:javascript
复制
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());
    }
}
代码语言:javascript
复制
进入线程前。。。
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后,就不能偏向了

代码语言:javascript
复制
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());
}

没有计算前

代码语言:javascript
复制
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)

计算后的

代码语言:javascript
复制
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

代码语言:javascript
复制
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));
}

轻量级锁和重量级锁对比

代码语言:javascript
复制
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位,但只有31位表示hashcode,对象头在windows上的表示要倒着看,然后最后一位表示对象的状态;
  3. 加锁就是改变对象头中的标志位
    1. 无锁 001
    2. 偏向锁 101
    3. 轻量级锁 000
    4. 重量级锁 010
  4. 计算过hashcode后,就不能再偏向了;
  5. 使用了wait方法,锁会立即变为重量级锁;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档