3

我正在尝试学习线程干扰的概念,并在 Java Tutorial Oracle 中遇到了以下示例:

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

}

Oracle 教程提到,如果有两个线程尝试访问变量c,可能会导致线程干扰,其中一个线程所做的更改不会被另一个线程看到。

但是,它没有提供任何代码来实际解释这个概念。有人可以提供一个基于Counter该类的代码示例来演示线程干扰是如何实际发生的吗?

4

3 回答 3

3

而不是代码,我更喜欢解释会发生什么。假设 2 个线程 A 和 B 正在访问同一个计数器对象A调用increment并且B调用decrement。这些操作中的任何一个都至少包含 3 个步骤。

  1. 从内存中读取C
  2. 递增或递减C
  3. 将C写回内存。

当 A 和 B 尝试同时递增和递减时,一个线程可能会从内存中读取C(步骤 1),而另一个线程则在步骤 2在这种情况下,如果c的初始值为5,首先线程 A 读取并将其递增到 6。然后线程 B 读取并将其递减到 4。请记住,B 在 A 完成将c写回内存之前进行这些更改。由于这些步骤是重叠的,一个线程所做的更改对另一个线程不可见,导致c的最终值为6 或 4。但实际上我们预期的是5

这是两个线程相互干扰的示例。为了避免这种情况,我们使用线程同步。

于 2016-02-28T04:38:24.343 回答
1

只需多次运行代码,在这些幸运时刻之一,您将能够看到线程干扰在起作用。

在这种情况下很少观察到,因为它是一个小程序并且没有发生太多事情。如果你制作多个递增和递减线程,线程干扰将更容易观察。

class Counter {
private int c = 0;

public void increment() {c++;}

public void decrement() {c--;}

public int value() {
    return c;
}

public static void main(String[] args) {

    Counter x = new Counter();
    Runnable r1 = new Runnable() {
        @Override
        public void run() {
            x.increment();
        }
    };

    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            x.decrement();
        }
    };

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);     
    t1.start();
    t2.start();
    System.out.println(x.c);

}

}

编辑:我决定添加多线程案例,我无法抗拒

编辑2:这是第二次编辑。多线程案例,因为这造成了超出这个问题范围的问题,我决定删除它。以前我正在制作一个线程数组并运行它们。相反,最好显示一个一个做很多增量的线程和另一个做很多减量的线程。

我使用了 Thread.Sleep() 导致主线程进入睡眠状态,这将确保在两个线程都完成对它的操作后打印 c 。

class Counter {
private int c = 0;

public void increment() {
    for (int i = 0; i < 10000; i++) {
        c++;
    }

    }

public void decrement() {
    for (int i = 0; i < 5000; i++) {
        c--;
    }
    }

public int value() {
return c;
}

public static void main(String[] args) {

Counter x = new Counter();
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        x.increment();
    }
};

Runnable r2 = new Runnable() {
    @Override
    public void run() {
        x.decrement();
    }
};

Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);     
t1.start();
t2.start();
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
if(!(t1.isAlive() && t2.isAlive()))
System.out.println(x.c);//expected answer 5000
}
}

注意:同步增量/减量方法给出正确答案。自己尝试。

于 2017-04-15T22:28:40.483 回答
1

创建多个线程并从这些线程调用increment(), decrement()和。value()

示例代码将如下所示:

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

    public static void main(String args[]){
        Counter c = new Counter();
        for ( int i=0; i<3; i++){
            Thread t = new Thread(new MyRunnable(c));
            t.start();
        }
    }
}
class MyRunnable implements Runnable{
    Counter counter;
    public MyRunnable(Counter c){
        counter = c;
    }
    public void run(){
        counter.increment();
        System.out.println("Counter value after increment:"+counter.value()+" from thread:"+  Thread.currentThread().getName());
        counter.decrement();
        System.out.println("Counter value after decrement:"+counter.value()+" from thread:"+  Thread.currentThread().getName());
    }
}

输出:(每次运行此输出都会有所不同)

Counter value after increment:1 from thread:Thread-0
Counter value after decrement:2 from thread:Thread-0
Counter value after increment:2 from thread:Thread-2
Counter value after decrement:1 from thread:Thread-2
Counter value after increment:3 from thread:Thread-1
Counter value after decrement:0 from thread:Thread-1

现在从输出中,您可以了解线程干扰。要解决此问题,您可以使用AtomicInteger

请查看以下帖子以获取更多详细信息:

为什么同步不能正常工作?

于 2017-04-17T18:26:14.100 回答