并发中锁的原理

    技术2022-07-11  84

    一篇对对象锁的原理,存储位置的笔记

    无锁:启动VM参数不配置,4s 程序开始执行,无锁 01偏向锁: JVM启动,默认延迟4s启动偏向锁。轻量级锁:synchronized (userEntity) 一个线程调用, 轻量级锁 00重量级锁:synchronized (userEntity) 多个线程争抢, 重量级锁 10

    演示锁的几种状态

    pom.xml <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.8</version> </dependency> static UserEntity userEntity ; // 立即开启偏向锁: -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 // JVM默认会延迟 4s 开启偏向锁, // 00000101 101 偏向锁 //轻量级锁 00 //重量级锁 10 //无锁 01 public static void main(String[] args) throws Exception { userEntity = new UserEntity(); out.println("before lock"); out.println(ClassLayout.parseInstance(userEntity).toPrintable()); // 此时 userEntity 无锁 Thread t1 = new Thread(()->{ synchronized (userEntity){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); Thread.sleep(1000); out.println("t1 lock ing"); out.println(ClassLayout.parseInstance(userEntity).toPrintable()); // 此时 userEntity此时 轻量级锁 sync(); out.println("after lock"); out.println(ClassLayout.parseInstance(userEntity).toPrintable()); //重量级锁 System.gc(); out.println("after gc()"); out.println(ClassLayout.parseInstance(userEntity).toPrintable()); // 无锁 } private static void sync() { synchronized (userEntity){ try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } } } result: before lock com.lxu.threadlocal.UserEntity 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 java.lang.String UserEntity.name null 16 4 java.lang.Integer UserEntity.age null 20 4 java.lang.String UserEntity.sex null 24 4 java.lang.String UserEntity.desc null 28 4 java.lang.Long UserEntity.create_time null Instance size: 32 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total t1 lock ing com.lxu.threadlocal.UserEntity object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 20 f5 9e 20 (00100000 11110101 10011110 00100000) (547288352) 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 java.lang.String UserEntity.name null 16 4 java.lang.Integer UserEntity.age null 20 4 java.lang.String UserEntity.sex null 24 4 java.lang.String UserEntity.desc null 28 4 java.lang.Long UserEntity.create_time null Instance size: 32 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total after lock com.lxu.threadlocal.UserEntity object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) ca 1c 41 1d (11001010 00011100 01000001 00011101) (490806474) 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 java.lang.String UserEntity.name null 16 4 java.lang.Integer UserEntity.age null 20 4 java.lang.String UserEntity.sex null 24 4 java.lang.String UserEntity.desc null 28 4 java.lang.Long UserEntity.create_time null Instance size: 32 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total after gc() com.lxu.threadlocal.UserEntity object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 09 00 00 00 (00001001 00000000 00000000 00000000) (9) // 00001001 第一位nouse,2-5 age 6 是否是偏向锁, 7-8 锁类别标识 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 java.lang.String UserEntity.name null 16 4 java.lang.Integer UserEntity.age null 20 4 java.lang.String UserEntity.sex null 24 4 java.lang.String UserEntity.desc null 28 4 java.lang.Long UserEntity.create_time null Instance size: 32 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

    00001001 第一位nouse,2-5 age ,6 是否是偏向锁, 7-8位 锁类别标识

    调用hashcode 方法后,对象头 前56位 就不能存放偏向锁信息java 对象头组成: mark_word 8byte 、klass_word 4bytemark_word 64bit 由 hash、gc标识、是否偏向锁、lock 组成, 但格式不固定 锁类型对象头信息描述偏向锁54bit threadinfo 2bit epoch、 gc 、1bit biasedlock轻量锁62-pro_to_record 、lock、重量锁62-ptr_to_monitorObject lock

    ① 无锁时 ② 偏向锁时 ③

    锁的膨胀过程

    如果调用wait()方法,会立即变成重量级锁两个线程逐个执行,依次为同一类的对象加锁多次,不满20次,锁升级 偏向锁-> 轻量锁 ,达20次,则jvm会把这个对象原来偏向的线程,全都指定为另一个线程,且这些对象都会不升级轻量锁,还是保持偏向锁, 偏向锁-> 轻量锁 复杂且费时 当jvm发现同一个线程,同一个类 在多次膨胀,就会发现这个类有问题,停止膨胀,批量偏量到指定线程 验证 // VM启动配置 立即开启偏向锁: -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 public static void main(String[] args) throws InterruptedException { List<A> list = new ArrayList<>(); System.out.println("t1 start..."); Thread t1 = new Thread(() -> { for (int i = 0; i < 25; i++) { System.out.println("111"); A a = new A(); synchronized (a){ //此时a 为偏向锁,偏向 t1线程 list.add(a); } } },"t1"); t1.start(); t1.join(); System.out.println("t2 before"); System.out.println(ClassLayout.parseInstance(list.get(1)).toPrintable()); Thread thread1 = new Thread(() -> { int k = 0; for (A a: list) { synchronized (a){ if (k == 21){ //达到20次后,list中所以a对象, 偏向t2线程,保持偏向锁状态 System.out.println("t2 ing"); System.out.println(ClassLayout.parseInstance(a).toPrintable()); } k++; } } },"t2"); thread1.start(); thread1.join(); System.out.println("t2 after"); System.out.println(ClassLayout.parseInstance(list.get(1)).toPrintable()); } result: t1 start... t2 before com.lxu.th.A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d8 aa 1f (00000101 11011000 10101010 00011111) (531290117) // 00000101 101 标识偏向锁 531290117 标识偏向的线程 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 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total t2 ing com.lxu.th.A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d9 bc 1f (00000101 11011001 10111100 00011111) (532470021) //达到20次以后,都为偏向锁,且偏向的线程ID 532470021 变了 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 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total t2 after //释放锁后 com.lxu.th.A object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) // 01 无锁 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 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    Processed: 0.014, SQL: 9