多线程同时访问一个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)

  即可!

  

最新文章

  1. LeetCode-3LongestSubstringWithoutRepeatingCharacters(C#)
  2. Redis命令拾遗一(字符串类型)
  3. C#实现DNS解析服务和智能DNS服务
  4. 【腾讯bugly干货】QQ空间直播秒开优化实践
  5. Android的Proxy/Delegate Application框架 (主要介绍插件化开发)
  6. C#获取指定网页源码的几种方法
  7. myeclipse连接数据库遇到的几个问题
  8. PHP用memcached做实时分页
  9. Java语言基础(三)
  10. Android学习笔记__1__Android体系架构
  11. 游戏 TRAP(SNRS)AlphaBeta版本
  12. 转 gl_VertexID的含义
  13. 5分钟了解MySQL5.7的Online DDL雷区
  14. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
  15. EBS中内部银行相关API
  16. springcloud 设置feign超时时间
  17. PS 十分钟教你做出文字穿插效果
  18. Python 之 __new__() 方法与实例化(转)
  19. 2.4 CSS定位
  20. arpg网页游戏特效播放(一)

热门文章

  1. centos8安装redis
  2. PHP SPL标准库-迭代器
  3. 生成流水号(20060210-0001)的SQL函数
  4. HTML 5标签中&lt;button&gt;的新属性
  5. MASM入门 (一)DOSBox的安装和使用
  6. StringUtils工具类(Apache lang3 )
  7. byte + byte = int
  8. Python ( 学习 基础篇第一部 )
  9. Pyqy5 让窗口居中
  10. pyqt5安装报错解决办法