你竟然用Integer作为synchronized的锁对象?
在使用多线程编程时,往往会使用一些手段保证线程安全,也就是加锁,但是加锁也必须合理,如使用synchronized对对象加锁时,如果不注意,还可能发生错误的加锁。
先看一段小测试,在这个小测试中,启动了1000个线程,每个线程在对integer加1前都先获得integer的锁,这看似是线程安全的,并且预期可能会得到1000这个值,而然并不然,在运行多次之后他总是输出<=1000的值,那么,这犊子是哪出问题了。
static Integer integer =new Integer("0");
static CountDownLatch mCountDownLatch =new CountDownLatch(1000);
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
new Thread(()->{
synchronized (integer){
integer++;
mCountDownLatch.countDown();
}
}).start();
}
mCountDownLatch.await();
System.out.println(integer);
}
这其实就是上述所说:"错误的加锁对象",也就是Integer作为锁对象这是个不正确的选择。
那在说Integer,不少人可能会见过这样的题。
Integer i1 =125;
Integer i2 =125;
System.out.println(i1==i2);
Integer i3 =135;
Integer i4 =135;
System.out.println(i3==i4);
答案是true,false,这样的结果是由Integer的缓存导致,在直接对Integer=xxx时候,其实调用了Integer.valueOf,(可以使用javap命令反编译查看)
而Integer.valueOf中,如果参数值在一定范围内,就从IntegerCache缓存中返回,也就是说在一定范围内多个相同的值是同一个对象,超出的话则return new Integer(i)返回一个新的Integer。而这个范围在-128-127,
在上面的135显然不在范围,则返回的是新对象,又由于==是比较内存地址,所以上述会出现false。如果要比较包装类是否相等,正确的做法是equals方法,Integer也重写了equals,判断内部基本数据类型是否一样。
这样一来,synchronized(integer)原因就知道了, integer++是创建了一个新的Integer对象,并将引用赋值给integer。因为integer一直在变,线程每次加锁可能都加在了不同对象的实例上,导致临界区控制出现问题。
解决办法也很容易,只要加在一个不变的对象上即可。
static Integer integer =new Integer("0");
static CountDownLatch mCountDownLatch =new CountDownLatch(1000);
static Object sObject= new Object();
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
new Thread(()->{
synchronized (sObject){
integer++;
mCountDownLatch.countDown();
}
}).start();
}
mCountDownLatch.await();
System.out.println(integer);
}
(不只Integer,Character、Short、Long也有缓存,但是Float和Double却没有)
原文地址:https://www.cnblogs.com/HouXinLin/p/12560047.html
- go http 服务器编程(1)
- Linux系统内存监控、性能诊断工具vmstat命令详解
- go http 服务器编程(2)
- 利用placeholder属性来添加输入框默认文字提示,提高用户体验
- Linux系统监控、诊断工具之top命令详解
- 【Dev Club分享】iOS黑客技术大揭秘
- Linux终端:用cat命令查看不可见字符
- golang 函数定义及其接口实例
- 分享两种圣诞节雪花特效JS代码(网站下雪效果)
- React 移动 web 极致优化
- golang 高效字符串拼接
- Linux+Nginx/Apache/Tomcat新增SSL证书,开启https访问教程
- golang 使用时间通过md5生成token
- golang中对map操作类
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 聊聊dubbo-go的leastActiveLoadBalance
- 聊聊dubbo-go的randomLoadBalance
- 聊聊dubbo-go的roundRobinLoadBalance
- PowerBI 不同日期区间的活动逐日对比分析
- flask SQLAlchemy查询数据库最近30天,一个月,一周,12小时或之前的数据
- TypeScript是什么,为什么要使用它?
- Python 基础(十):模块与包
- 谷歌浏览器一些https打不开点击高级不行的解决办法
- CV入门赛最全思路&上分技巧汇总!
- VBA专题10-5:使用VBA操控Excel界面之隐藏/取消隐藏及最小化功能区
- 从一道面试题说起:GET 请求能传图片吗?
- ModuleNotFoundError: No module named ‘pip‘【已解决】
- 【原创】Java并发编程系列31 | 阻塞队列(上)
- 【2019-3-3】Mac启动redis
- awvs(acunetix)使用一段时间后突然不能用了-解决方案