当前位置:   article > 正文

并发编程之共享带来的问题的详细解析

并发编程之共享带来的问题的详细解析

4.1共享带来的问题

Java代码示例

两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?

  1. static int counter = 0;
  2. public static void main(String[] args) throws InterruptedException {
  3.    Thread t1 = new Thread(() -> {
  4.        for (int i = 0; i < 5000; i++) {
  5.            counter++;
  6.       }
  7.   }, "t1");
  8.    Thread t2 = new Thread(() -> {
  9.        for (int i = 0; i < 5000; i++) {
  10.            counter--;
  11.       }
  12.   }, "t2");
  13.    t1.start();
  14.    t2.start();
  15.    t1.join();
  16.    t2.join();
  17.    log.debug("{}",counter);
  18. }
问题分析

以上的结果可能是正数、负数、零。为什么呢?因为 Java 中对静态变量的自增,自减并不是原子操作,要彻底理解,必须从字节码来进行分析

例如对于 i++ 而言(i 为静态变量),实际会产生如下的 JVM 字节码指令:

  1. getstatic i // 获取静态变量i的值
  2. iconst_1 // 准备常量1
  3. iadd // 自增
  4. putstatic i // 将修改后的值存入静态变量i

而对应 i-- 也是类似:

  1. getstatic i // 获取静态变量i的值
  2. iconst_1 // 准备常量1
  3. isub // 自减
  4. putstatic i // 将修改后的值存入静态变量i

而 Java 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:

如果是单线程以上 8 行代码是顺序执行(不会交错)没有问题:

但多线程下这 8 行代码可能交错运行: 出现负数的情况:

出现正数的情况:

临界区 Critical Section
  • 一个程序运行多个线程本身是没有问题的

  • 问题出在多个线程访问共享资源

    • 多个线程读共享资源其实也没有问题

    • 在多个线程对共享资源读写操作时发生指令交错,就会出现问题

  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区

  1. static int counter = 0;
  2. static void increment()
  3.    // 临界区
  4. {
  5.    counter++;
  6. }
  7. static void decrement()
  8.    // 临界区
  9. {
  10.    counter--;
  11. }
竞态条件 Race Condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/290214
推荐阅读
相关标签
  

闽ICP备14008679号