Integer 错误的加锁
多线程同时访问一个Integer加锁的问题,程序运行和想要的结果相差甚远,让我百思不得其解,就下来研究了一下:
在进行多线程同步时,加锁是保证线程安全的重要手段之一。synchronized是大多数程序员必须要掌握的同步锁,但是这个问题非常的隐晦,大家可以参考一下:
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 < 10000000; 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);
}
}
程序运行结果:
12953898
注意:结果和我们想要的结果相差甚远,得到的是一个比20000000 小很多的数字。这说明一定是这段程序并没有真正做到线程安全!但把锁加在变量 i 上似乎逻辑也是无懈可击的,为啥得不到准确的结果呢?问题就在这一块,synchronized(i) 底层有问题!
要解释这个问题,得从Integer说起。在Java中,Integer是不可变对象。也就是对象一旦创建了就不可能被修改!
public final class Integer extends Number implements Comparable<Integer> {
......
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i); //这个方法会新创建一个新的Integer对象
}
private final int value; //封装的value变量 final关键字修饰,不可变
......
}
如果我们使用javap反编译这段代码的 run() 方法,我们可以看到:

在第19 ~ 22 行,实际上使用了 Integer.valueOf(i) 方法新建了一个新的 value 对象,并将它赋值给变量 i。也就是说 i++ 变成了:
i = Integer.valueOf(i.intValue() + 1); //涵盖了包装类与基本类型的装箱和拆箱原理成分
进一步查看 Integer.valueOf(),我们可以看到:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Integer.valueOf() 实际上是一个工厂方法,它会倾向于返回一个代表指定数值的 Integer 实例。因此,i++ 的本质是创建一个新的 Integer 对象,并将它的引用赋值给 i 。
如此一来,我们就可以明白问题所在了,由于在多个线程间,并不一定能够看到同一个 i 对象(因为 i 对象一直在变),因此,两个线程每次加锁可能都加在了不同的对象实例上,从而导致对临界区代码控制出现问题。
修改这个问题也很容易,只需要将
synchronized(i)
改为:
synchronized(instance)
即可!
Integer 错误的加锁的更多相关文章
- log4j:ERROR Category option " 1 " not a decimal integer.错误解决
log4j.properties 的配置文件中: log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }: ...
- Java并发程序设计(二)Java并行程序基础
Java并行程序基础 一.线程的生命周期 其中blocked和waiting的区别: 作者:赵老师链接:https://www.zhihu.com/question/27654579/answer/1 ...
- java并发实践笔记
底层的并发功能与并发语义不存在一一对应的关系.同步和条件等底层机制在实现应用层协议与策略须始终保持一致.(需要设计级别策略.----底层机制与设计级策略不一致问题). 简介 1.并发简史.(资源利用率 ...
- JAVA学习基础知识总结(原创)
(未经博主允许,禁止转载!) 一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平 ...
- Java基础总结大全
一.基础知识: 1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平台,jvm不是跨平台的. J ...
- Redis缓存穿透、缓存雪崩、并发问题分析与解决方案
(一)缓存和数据库间数据一致性问题 分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存.我们只能 ...
- 2 java并行基础
我们认真研究如何才能构建一个正确.健壮并且高效的并行系统. 进程与线程 进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础 ...
- JAVA基础面试汇总
一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java ...
- 剑指offer刷题笔记
删除链表中重复的结点:较难 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4- ...
随机推荐
- spring boot:用spring security加强spring boot admin的安全(spring boot admin 2.3.0 / spring boot 2.3.3)
一,spring boot admin的安全环节: 1,修改context-path,默认时首页就是admin, 我们修改这个地址可以更安全 2,配置ip地址白名单,有ip限制才安全, 我们使用了sp ...
- spring boot:在项目中引入第三方外部jar包集成为本地jar包(spring boot 2.3.2)
一,为什么要集成外部jar包? 不是所有的第三方库都会上传到mvnrepository, 这时我们如果想集成它的第三方库,则需要直接在项目中集成它们的jar包, 在操作上还是很简单的, 这里用luos ...
- spring boot: filter/interceptor/aop在获取request/method参数上的区别(spring boot 2.3.1)
一,filter/interceptor/aop在获取参数上有什么区别? 1,filter可以修改HttpServletRequest的参数(doFilter方法的功能), interceptor/a ...
- selenium 浏览器标签切换
from time import sleep from selenium import webdriver from selenium.webdriver.chrome.options import ...
- python中实现格式化输入(史上最简单、最高效的实现方法,不借助任何模块)
今天我在写python作业时突然想到格式化输入一段文字,譬如只需读取输入的前几个字符就行,而不幸的是,python中的输入并没有c中的read().getchar()函数,于是我网上搜了一下,网上的解 ...
- Spring 缓存抽象
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术:并支 ...
- 【CF1436C】Binary Search 题解
原题链接 题意简介 要求有多少种 n 的排列,能够通过二分法正确地找到放在 pos 处的数字 x. 答案对 1e9+7 取模.n<=1000. 采用的二分法如下图: 思路分析 首先,这个排列中有 ...
- STM32入门系列-STM32外设地址映射
片上外设区分为四条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1挂载低速外设,APB2和AHB挂载高速外设.相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外 ...
- error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools“
python3 是用 VC++ 14 编译的, python27 是 VC++ 9 编译的, 安装 python3 的包需要编译的也是要 VC++ 14 以上支持的. 可以下载安装这个: 链接:htt ...
- XJOI 夏令营501-511NOIP训练18 高三楼
参观完各种饭堂,学校还有什么著名的景点呢?当然是教室了,此时此刻我 们来到了高三楼.你会发现高三楼门口会有以身份认证系统,这东西还有着一段疼人的历史.每年的九月到来,高三的童鞋大多不习惯学校的作息时间 ...