• 可见性、原子性和有序性问题:并发编程Bug的源头


    1)如何快速而又精准地解决“并发”类的疑难杂症?

    • 理解这件事情的本质,追本溯源,深入分析这些 Bug 的源头在哪里。

    2)CPU、内存、I/O 设备的速度差异有多大?

    • CPU 是天上一天,内存是地上一年(假设 CPU 执行一条普通指令需要一天,那么 CPU 读写内存得等待一年的时间)

    • 内存和 I/O 设备的速度差异就更大了,内存是天上一天,I/O 设备是地上十年。

    3)为了平衡这三者的速度差异,设计者们做了哪些努力?

    • CPU 增加了缓存,以均衡与内存的速度差异;

    • 操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;

    • 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。

    4)我们的程序都在享受着前辈们带来的速度上的优化,但是天下没有免费的午餐,缩小贫富差距会带来哪些问题?

    • 缓存导致的可见性问题

    • 线程切换带来的原子性问题

    • 编译优化带来的有序性问题

    5)什么是可见性?

    • 一个线程对共享变量的修改,另外一个线程能够立刻看到

    6)为什么缓存会带来可见性的问题?

    • 以前穷的时候,就一个CPU,一块缓存,所有的线程都是他来执行的,都共享他里面的数据,数据和内存里面的是一致的。

    • 现在日子好过了,有多个CPU,每个CPU有自己的缓存。多个线程是在不同的CPU上面执行的,操作的是不同的CPU缓存数据。如果线程A操作完CPU1的缓存数据,但是没有及时向内存更新,那我线程B是看不见的。

    7)思考一下,下面两个线程执行完之后,count值为多少?

     public class Test {
       private long count = 0;
       private void add10K() {
         int idx = 0;
         while(idx++ < 10000) {
           count += 1;
        }
      }
       public static long calc() {
         final Test test = new Test();
         // 创建两个线程,执行add()操作
         Thread th1 = new Thread(()->{
           test.add10K();
        });
         Thread th2 = new Thread(()->{
           test.add10K();
        });
         // 启动两个线程
         th1.start();
         th2.start();
         // 等待两个线程执行结束,插的是main线程的队
         th1.join();
         th2.join();
         return count;
      }
     }

    8)什么是原子性?

    • 一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性

    9)什么是原子性问题?

    • 你看着一条语句执行是不可分的,是整体。但是在CPU眼中,却是很多步才能执行完。

    10)count+=1这条高级语言指令我们的CPU需要几步才能完成?

    • setp1: 把count变量从内存加载到CPU的寄存器

    • stpe2: 在寄存器中执行+1操作

    • step3: 把结果写入内存(缓存机制导致可能写入的是CPU缓存而非内存)

    11)为什么说我们的CPU分时带来了原子性的问题?

    • 假如现在有两个线程A和B,都执行上面的语句。A执行完step1之后让出时间片,B去执行了,B执行完之后又换到A来执行后面的2,3步骤。那结果和我们的预期结果2不相符。

    12)什么是有序性?

    • 程序按照代码的先后顺序执行

    13)什么是有序性问题?

    • 编译器为了优化性能,有时候会改变程序中语句的先后顺序,例如程序中:“a=6;b=7;”编译器优化后可能变成“b=7;a=6;”

    14)因为编译器悄悄改变我们程序执行顺序而出bug的有哪些案例?

    • 单例模式双重检测的时候编译器改变了我们对象初始化的顺序导致出现空指针异常。

    15)为什么long型变量在32位机器上进行加减操作存在并发隐患?

    • Long占8个字节,64位,64位的到32位机器上就得拆分命令了,容易出现原子性的问题。

  • 相关阅读:
    HDU 4686
    二叉索引树——树状数组
    poj 3548 Restoring the digits(DFS)
    poj 2062 Card Game Cheater(排序+模拟)
    poj 2570 Fiber Network(floyd)
    hdu 1080 Human Gene Functions
    hdu 4512 吉哥系列故事——完美队形I(最长公共上升自序加强版)
    2015 Multi-University Training Contest 2
    poj 1258 Agri-Net(最小生成树)
    2015 Multi-University Training Contest 1记录
  • 原文地址:https://www.cnblogs.com/YXBLOGXYY/p/15962083.html
Copyright © 2020-2023  润新知