场景描述:多线程输出1到100,对静态Integer对象加锁,synchronized代码块中操作Integer对象,发生线程安全问题(数据重复)

代码:

public class MyRunnableTest implements Runnable {
public static Integer i = new Integer(0); @Override
public void run() {
while(true){
synchronized (i) {
if(i<100){
i++;
System.out.println(Thread.currentThread()+"i = " + i);
}else {
break;
}
}
}
} public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnableTest());
Thread t2 = new Thread(new MyRunnableTest());
t1.start();
t2.start();
} }

运行结果:

Thread[Thread-0,5,main]i = 1
Thread[Thread-1,5,main]i = 3
Thread[Thread-1,5,main]i = 4
Thread[Thread-1,5,main]i = 5
Thread[Thread-1,5,main]i = 6
Thread[Thread-0,5,main]i = 5
Thread[Thread-1,5,main]i = 7
Thread[Thread-0,5,main]i = 8
Thread[Thread-1,5,main]i = 9
Thread[Thread-0,5,main]i = 10
Thread[Thread-1,5,main]i = 11
Thread[Thread-0,5,main]i = 12
Thread[Thread-1,5,main]i = 13

 

从运行结果中可以发现发生了线程安全问题,为什么呢?为什么synchronized无效了。

我的排查思路:

1、因为没有进行任何的额外操作,所以首先定位问题在i++处

通过javap分析字节码命令,总结:

可以看出:i++的实际操作为:Integer.valueOf(Integer.intValue(i)+1)

我们在看看Integer中vauleOf的源码:

public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} cache[]数组初始化: 
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

其中low为-128,hight为127。可以了解到,当i的值为-128~127时,是从IntegerCache中取的(可以理解为从缓存中取的),超过部分是new出来的对象。

2、目前我们慢慢开始解开了问题的面纱,每当i发生自增后,所对象改变了,这里我们还需要清楚一下,synchronized在锁对象发生改变时(测试发现,引用类型对象的内在属性变化不会释放锁)会立即释放锁。所以这里就会出现线程安全问题,而且在单核的运行环境下,所有的线程是并发执行而不是并行执行,当我们运行到system.out.println时,锁已经释放了,假如这边t1线程(当前运行的线程,i=1的情况)释放CPU资源,t2执行,这时i等于2(i++之前),当执行到system.out.println时释放cpu资源(此时i=3),t1执行,i此时已经为3了,所以输出3,在释放cpu资源,t2执行,输出3,这时出现了输出重复值的情况。

synchronized 加锁Integer对象(数据重复)详解的更多相关文章

  1. ContentProvider数据访问详解

    ContentProvider数据访问详解 Android官方指出的数据存储方式总共有五种:Shared Preferences.网络存储.文件存储.外储存储.SQLite,这些存储方式一般都只是在一 ...

  2. 【转载】图说C++对象模型:对象内存布局详解

    原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在 ...

  3. Scala 深入浅出实战经典 第64讲:Scala中隐式对象代码实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  4. 【HANA系列】SAP HANA XS使用JavaScript数据交互详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用Jav ...

  5. JVM 运行时数据区详解

    一.运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同数据区域. 1.有一些是随虚拟机的启动而创建,随虚拟机的退出而销毁,所有的线程共享这些数据区. 2.第二种则 ...

  6. 【HANA系列】【第一篇】SAP HANA XS使用JavaScript数据交互详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第一篇]SAP HANA XS ...

  7. CEPH-4:ceph RadowGW对象存储功能详解

    ceph RadosGW对象存储使用详解 一个完整的ceph集群,可以提供块存储.文件系统和对象存储. 本节主要介绍对象存储RadosGw功能如何灵活的使用,集群背景: $ ceph -s clust ...

  8. Jquery遍历筛选数组的几种方法和遍历解析json对象|Map()方法详解

    Jquery遍历筛选数组的几种方法和遍历解析json对象|Map()方法详解 一.Jquery遍历筛选数组 1.jquery grep()筛选遍历数组 $().ready( function(){ v ...

  9. java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解

    java使用POI操作XWPFDocument中的XWPFRun(文本)对象的属性详解 我用的是office word 2016版 XWPFRun是XWPFDocument中的一段文本对象(就是一段文 ...

随机推荐

  1. 5 种使用 Python 代码轻松实现数据可视化的方法

    数据可视化是数据科学家工作中的重要组成部分.在项目的早期阶段,你通常会进行探索性数据分析(Exploratory Data Analysis,EDA)以获取对数据的一些理解.创建可视化方法确实有助于使 ...

  2. 软件工程第八周PSP

    1.本周PSP 2.本周进度条 代码行数 0 博文字数 356 知识点 无 3.时间分配饼状图 4.折线图

  3. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  4. Codeforces Round #335 (Div. 2) D. Lazy Student 贪心+构造

    题目链接: http://codeforces.com/contest/606/problem/D D. Lazy Student time limit per test2 secondsmemory ...

  5. 老司机带你解读jQuery插件开发流程

    jquery插件开发模式 jquery插件一般有三种开发方式: 通过$.extend()来扩展jQuery 通过$.fn 向jQuery添加新的方法 通过$.widget()应用jQuery UI的部 ...

  6. 单机安装 consul ui 对外提供服务

    Consul 安装启动ui,外网无法访问,应为Consul 默认绑定127.0.0.1 ,所以外网无法访问. 通过设置 -client 参数来设置     consul agent -server - ...

  7. 使用git下载编译erlang

    git clone https://github.com/erlang/otp cd otp git tag git checkout -b OTP- OTP- ./otp_build all exp ...

  8. 微信Web开发者工具-下载、安装和使用图解

    开发和测试小程序,需要借助微信官方提供的微信Web开发者工具进行预览和调试代码,从下载安装到使用,大致的流程如下: 1.下载安装包 下载地址传送门:https://developers.weixin. ...

  9. laravel5.6 调用第三方类库

    大概流程: 1. 新建一个目录方类库 2. 配置composer配置文件 3. 在项目中使用终端运行composer  dumpautoload 4. 使用时 方法调用可以new对象后->方法名 ...

  10. Python @retry装饰器的使用与实现案例(requests请求失败并重复请求)

    在爬虫代码的编写中,requests请求网页的时候常常请求失败或错误,一般的操作是各种判断状态和超时,需要多次重试请求,这种情况下,如果想优雅的实现功能,可以学习下retrying包下的retry装饰 ...