博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
由一个多线程共享Integer类变量问题引起的。。。
阅读量:4081 次
发布时间:2019-05-25

本文共 1747 字,大约阅读时间需要 5 分钟。

假设并发环境下,业务代码中存在一些统计操作,为了保证线程安全,开发人员往往会对计数值进行加锁(synchronized),值得注意的是,直接对Integer类型进行加锁,似乎并会达到预期效果,比如下面这段代码:

Integer num = new Integer(0);

public void test() throws InterruptedException {

final int THREAD_SIZE = 10;

final int TASK_SIZE = 100000;

final CountDownLatch latch = new CountDownLatch(THREAD_SIZE);

for (int i = 0; i < THREAD_SIZEi++) {

new Thread() {

public void run() {

for (int j = 0; j < TASK_SIZE / THREAD_SIZEj++) {

synchronized (num) {

num++;

}

}

latch.countDown();

}

}.start();

}

latch.await();

System.out.println("num-->" + num);

}

 

上述代码示例中,总共有10个线程运行,每个线程执行次数为10000(taskSize/threadSize),但是实际程序输出结果却并非是10W次,或许有些同学会觉得诧异,在对计数值numInteger)进行递增操作前,已经执行了加锁操作,为啥还是非线程安全。我们首先来看一下上述程序的线程堆栈信息:

 

 

上图中,每一个线程锁住的资源其实都并非是同一个,这就可以解释为什么对Integer类型进行加锁仍然是非线程安全的

 

或许有同学会说,JavaAPI提供有线程安全的AtomicInteger为啥不用,尽管AtomicInteger是线程安全的,但是接下来我们还是要聊一聊为啥锁不住Integer等原始数据类型的封装类型。

 

JAVA5为原始数据类型提供了自动装/拆箱功能,假设对Integer进行递增/递减操作后,其实HashCode已经发生了变化,synchronized自然也不是同一个对象实例,Integer的源码,如下所示:

 

 

从源码中可以看出,当为Integer赋值数值在-128~127区间时,会从Integer中的一个Integer[]中获取一个缓存的Integer对象,而超出区间值得时候,每次都会new一个新的Integer对象,假设下述这段代码:

Integer num = new Integer(300);

System.out.println("identityHashCode-->" + System.identityHashCode(num));

System.out.println("hashCode-->" + num.hashCode());

num = 300;

System.out.println("identityHashCode-->" + System.identityHashCode(num));

System.out.println("hashCode-->" + num.hashCode());

 

实际程序输出为:

identityHashCode-->627248822

hashCode-->300

identityHashCode-->523481450

hashCode-->300

 内存改变了!即Integer类型超出[-128~127]区间时值的变动都会带来内存的变动

 

Synchronized锁的是对象,也就是identityHashCode所指向的内存地址中的对象实例(根据对象内存地址生成散列值),而hashcode输出的是值得散列值。所以为啥上述程序示例中,identityHashCode每次不同,而hashCode输出的值却相同

 

最后总结下,synchronized(Integer)时,当值发生改变时,基本上每次锁住的都是不同的对象实例,想要保证线程安全,推荐使用AtomicInteger之类会更靠谱。

转载地址:http://hntni.baihongyu.com/

你可能感兴趣的文章
C++报错:C4700:使用了非初始化的局部变量
查看>>
C++类、结构体、函数、变量等命名规则详解
查看>>
C++ goto语句详解
查看>>
【数据结构周周练】008 二叉树的链式创建及测试
查看>>
《软件体系结构》 第九章 软件体系结构评估
查看>>
《软件体系结构》 第十章 软件产品线体系结构
查看>>
《软件过程管理》 第六章 软件过程的项目管理
查看>>
《软件过程管理》 第九章 软件过程的评估和改进
查看>>
分治法 动态规划法 贪心法 回溯法 小结
查看>>
《软件体系结构》 练习题
查看>>
《数据库系统概论》 第一章 绪论
查看>>
《数据库系统概论》 第二章 关系数据库
查看>>
《数据库系统概论》 第三章 关系数据库标准语言SQL
查看>>
SQL语句(二)查询语句
查看>>
SQL语句(六) 自主存取控制
查看>>
《计算机网络》第五章 运输层 ——TCP和UDP 可靠传输原理 TCP流量控制 拥塞控制 连接管理
查看>>
堆排序完整版,含注释
查看>>
二叉树深度优先遍历和广度优先遍历
查看>>
生产者消费者模型,循环队列实现
查看>>
PostgreSQL代码分析,查询优化部分,process_duplicate_ors
查看>>