2021-09-12

java多线程锁解析(一)

首先,先来看一个最简单的java对象在jvm中加载后的二进制分布,简单展示一下代码

**
 * @Author xuzzhh
 * @Date 2021/9/12 16:20
 * @Version 1.0
 * @Since study
 */
public class ObjectHead {
    static  LHead lHead = new LHead();
    public static void main(String[] args){
        System.out.println("这是原有的对象头"+ClassLayout.parseInstance(lHead).toPrintable())}
   
    }
}
/**
 * @Author xuzzhh
 * @Date 2021/9/12 16:21
 * @Version 1.0
 * @Since study
 */
public class LHead {
}

执行效果如图所示
在这里插入图片描述

A区域中包含了未使用(1)+对象年龄(4)+是否可偏向(1)+锁类型(2)
途中00000101 表示对象年龄为0,可偏向,01表示偏向锁(java对象类型初始化时都是01偏向锁)
而在紧接A区域后边的地方会有两种情况:
(1):对象如果计算HashCode后的Hash值
(2):加锁后的持锁的线程ID
演示代码如下:

/**
 * @Author xuzzhh
 * @Date 2021/9/12 16:20
 * @Version 1.0
 * @Since study
 */
public class ObjectHead {
    static LHead lHead = new LHead();

    public static void main(String[] args) {
        System.out.println("这是原有的对象头" + ClassLayout.parseInstance(lHead).toPrintable());

        synchronized (lHead) {
            System.out.println(ClassLayout.parseInstance(lHead).toPrintable());
            System.gc();//手动进行一个GC,便于展现出对象的年龄变化
            System.out.println(ClassLayout.parseInstance(lHead).toPrintable());
        }
        System.out.println("这是Hash value" + Integer.toHexString(lHead.hashCode()));
        System.out.println("这是Hash 对象头" + ClassLayout.parseInstance(lHead).toPrintable());

    }
}

执行效果如下图:
在这里插入图片描述
从图上可以看出四种情况对应的执行结果,
(1):第一种为正常初始化后的对象头0(未使用) 0000(对象年龄为0) 1(可偏向) 01(偏向锁) ,线程ID或者Hashcode均为0。
(2):第二种执行结果为加锁后的执行结果,加锁后的对象结果变为对象头0(未使用) 0000(对象年龄为0) 1(可偏向) 01(偏向锁),与第一次执行结果不同的是,第二次出现了线程持有ID(00101000 01101100 00000011)对应的十六进制地址为(28 6c 03),这表示这个对象已经被一个线程持有锁了,并且从头部对象01可知,持有的是偏向锁。
(3):第三种执行结果是在二的情况下,手进行了一次GC,所以其对象的年龄进行了加1,其对象头的结果变为对象头0(未使用) 0001(对象年龄为1) 1(可偏向) 01(偏向锁),其余效果相对第二种情况无变化。
(4):第四种情况为执行了HashCode计算后的对象头0(未使用) 0001(对象年龄为1) 0(不可偏向) 01(偏向锁),计算了HashCode后,00110111 01101100 0110010000111101 后边的线程持有ID也变成了HashCode,将二进制转换为16进制后是37 6c 64 3d 由于计算机使用小端存储,所以实际的HashCode值为3d646c37,可以看见,计算HashCode后,对象头中的是否可偏向变成了不可偏向,而线程持有ID则变成了HashCode的值。
总结:
(1):当一把锁第一次进行加载的时候,对象头中出现的是偏向锁,当线程再次持有锁的时候依然会是偏向锁。
(2):当两个线程线程1和线程2交替执行时,对象头中的锁会膨胀为轻量级锁,
注:对象如果计算过HashCode后,可偏向标志为0,则表示不可偏向,此时即使是单个线程首次加锁,或者是两个线程交替执行持有对象锁,该锁依然会膨胀为轻量锁,所以在一般情况需要使用到偏向锁的时候,尽量不要对对象进行HashCode计算。
(3):当两个线程发生了资源竞争的时候,锁会膨胀为重量锁。
验证会在下一次更新中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值