线程混淆

考虑一个简单的名叫Counter的类。

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }
}

Counter类被设计成如下:每次调用increment方法,都会使c加一,每次调用decrement方法,都会使c减一。但是,如果一个Counter对象被多个线程引用,多个线程之间的混淆会导致功能的失常。

当两个运行于不同线程,却对同样的数据处理的操作出现时,混淆就发生了。这意味着有多个子步骤组成的操作,它们的顺序重叠了。

看上去,对Counter对象的操作不可能会重叠,因为两个对变量c的操作都是单句的简单陈述。但是,即使是简单的语句,虚拟机也会把它翻译成几个步骤。比如,C++这句语句,会被分成三个步骤:

  1. 获取当前c的值。
  2. 将c的值加1。
  3. 将增加过的c存回到内存中。

c--的操作亦然。

假设线程A调用了increment方法,而几乎同时线程B调用了decrement方法。如果c的初始值是0,两个线程混淆的操作可能是这样的顺序:

  1. 线程A:获取c。
  2. 线程B:获取c。
  3. 线程A:对c进行加一操作,结果是1。
  4. 线程B:对c进行减一操作,结果是-1。
  5. 线程A:把结果存回c中,c现在的值是1。
  6. 线程B:把结果存回c中,c现在的值是-1。

线程A的结果丢失了,被线程B覆盖写了。上面的这种混淆,只是一种可能性。在不同的情况下,也可能会变成线程B的结果丢失了,或者也可能没有错误。由于这是无法预料的,线程混淆的bug很难找到和修复。

你可能感兴趣的:(线程混淆)