JDK源码笔记之Integer深度解析以及并发下错误的加锁方式【踩坑笔记】

对于Java的数据类型Integer,大多人认为不就很简单的一个数据类型吗?是的,我以前也不在意,感觉关于integer没什么可说,但是不深入源码你永远不知道它给你留下的坑。

一、谈谈integer留下的坑
首先我们来看一段代码。

package cn.shinelon.jvm;

public class TestInteger {
    public static void main(String[] args) {
        Integer i1=1;
        Integer i2=1;
        System.out.println(i1==i2);
        Integer i3=129;
        Integer i4=129;
        System.out.println(i3==i4);
    }
}

上面的代码只要接触过Java的人都能够看明白,不用过多解释,比较两个integer变量是否相等。估计一眼就可以看出答案,但是对不对呢?可能与大多人所预料的结果大相径庭,大多人觉得两个结果输出一定都是true,但是我们来看看运行结果。
这里写图片描述

这个结果着实让很多大吃一惊,为什么第二个会是false呢,i3和i4不都是129吗?怎么不相等了,下面来让我们看看这其中的原因:
使用javap -c命令反编译class文件:
这里写图片描述

我们可以看到Integer i1,i2,i3,i4这些语句都调用了Integer.valueOf()方法,因此,我们来看看valueOf()方法的源码。
这里写图片描述
这里写图片描述

上面的代码是Integer的部分源码,看valueOf就可以知道上面结果的原因了。下面我们来系统的的梳理一下integer的内部原理实现:

首先,初始化时该缓冲区大小为128+127+1=256的数组,当你声明一个integer变量是它会调用valueOf(int i)方法,当i>IntegerCache.low&&IntegerCache.high时它会去从缓冲区中根据索引下标直接取出相应的值,而当超出这个范围时它就会new一个新的对象。这就很好解释刚开始的问题了,“==”对于基本数据类型比较的是值是否相等,对于Integer它比较的是两个引用变量的内存地址的首地址是否相等,对于i1和i2他们的值都等于1,因此在调用valueof函数的时候他们没有超出缓冲区的范围,因此返回的是同一个内存地址的值,而i3和i4他们的值是129,刚好超出范围(-128~128),所以valueof会new一个新的integer对象,它们是不同的对象,因此首地址指向不同的内存,因此是false。

在通过上面的了解之后我们看到integer的内部原理,这里我们接着再看看integer在并发情况下的一些错误加锁方式:

package cn.just.thread.concurrent;

public class BadLockOnInteger implements Runnable{
    public static Integer i=0;
    public static BadLockOnInteger instance=new BadLockOnInteger();
    @Override
    public void run() {
        for(int j=0;j<10000;j++){
        synchronized (i) {
            i++;
        }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(instance);
        Thread t2=new Thread(instance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

对于上面这段代码运行结果,这里就不卖关子了,理论情况下很多人看上去认为结果是20000,但是事实不是这样,结果会等到一个比20000小很多的数,比如12698,这是为什么呢?我们可以按照上面的方式使用javap命令反编译run()方法里面的代码,发现它任然调用了valueof方法,这就很好解释了,因为在该方法里它会在超出缓冲区范围后new一个新的对象,也就是说synchronized关键字加在两把不同的锁上面,因此两个线程不能同步,所以结果会比20000小很多。
怎么解决以上的问题呢?很简单,只需要将上面代码改为synchronized(instance)即可。

上面是个人对Integer的一些小小的总结以及解读,如有不足之处还请指教!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值