多线程同时访问一个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 错误的加锁的更多相关文章

  1. log4j:ERROR Category option " 1 " not a decimal integer.错误解决

    log4j.properties 的配置文件中: log4j.appender.stdout.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{ 1 }: ...

  2. Java并发程序设计(二)Java并行程序基础

    Java并行程序基础 一.线程的生命周期 其中blocked和waiting的区别: 作者:赵老师链接:https://www.zhihu.com/question/27654579/answer/1 ...

  3. java并发实践笔记

    底层的并发功能与并发语义不存在一一对应的关系.同步和条件等底层机制在实现应用层协议与策略须始终保持一致.(需要设计级别策略.----底层机制与设计级策略不一致问题). 简介 1.并发简史.(资源利用率 ...

  4. JAVA学习基础知识总结(原创)

    (未经博主允许,禁止转载!) 一.基础知识:1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平 ...

  5. Java基础总结大全

    一.基础知识: 1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. java语言是跨平台,jvm不是跨平台的. J ...

  6. Redis缓存穿透、缓存雪崩、并发问题分析与解决方案

    (一)缓存和数据库间数据一致性问题 分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存.我们只能 ...

  7. 2 java并行基础

    我们认真研究如何才能构建一个正确.健壮并且高效的并行系统. 进程与线程 进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础 ...

  8. JAVA基础面试汇总

    一.基础知识:1.JVM.JRE和JDK的区别:    JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性.                  java ...

  9. 剑指offer刷题笔记

    删除链表中重复的结点:较难 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4- ...

随机推荐

  1. centos8上配置openssh的安全

    一,openssh服务版本号的查看 1,查看当前sshd的版本号 : [root@yjweb ~]# sshd --help unknown option -- - OpenSSH_7.8p1, Op ...

  2. javascript 数字 字母 互转

    var alphabet= String.fromCharCode(64 + parseInt(填写数字); 单个字符转数字: 'a'.charCodeAt(0) 结果: 97 数字转字母: Stri ...

  3. Java中四舍五入保留两位小数

    Java中四舍五入保留两位小数 方法一 四舍五入 double f = 3.15; long res = Math.round(f); #结果 res = 3 保留两位小数 double f = 3. ...

  4. Cisco思科模拟器交换机划分VLAN 入门详解 - 精简归纳

    Cisco思科模拟器交换机划分VLAN 入门详解 - 精简归纳 JERRY_Z. ~ 2020 / 10 / 24 转载请注明出处!️ 附: 交流方式: ️ ️ ️ QQ: 1846334075 We ...

  5. springcloud中使用dubbo开发rpc服务及调用

    spring cloud中基于springboot开发的微服务,是基于http的rest接口,也可以开发基于dubbo的rpc接口. 一,创建goodsService模块 1, 在创建的goodsSe ...

  6. c# vs2019 Window服务搭建

    一.创建一个Windows Service         我的IDE开发工具使用的是vs2019,和之前的vs版本不大相同,所有添加了详细的截图说明.         首先,选择新建项目,项目类型选 ...

  7. 前端小程序——js+canvas 给图片添加水印

    市场上各种各样的图片处理器有很多,那么作为程序员的我们是不是应该自己做一个呢?那就从加水印开始吧 html: <canvas id="shuiyinTest"> < ...

  8. Java基础循环结构

    int x=10; do { System.out.println("value of x:"+x); x++; } while(x<20); //do while循环 1 ...

  9. 时间UTC格式装换php时间格式

    date_default_timezone_get("UTC");date("Y-m-d", strtotime("2017-11-13T18:04: ...

  10. Python实现微信支付(三种方式)

    Python实现微信支付(三种方式) 关注公众号"轻松学编程"了解更多. 如果需要python SDk源码,可以加我微信[1257309054] 在文末有二维码. 一.准备环境 1 ...