2016-02-28 16 views
1

我努力学习线程干扰的概念,并遇到了Java教程甲骨文下面的例子:线程干扰是如何在`Counter`示例类中实际发生的?

class Counter { 
    private int c = 0; 

    public void increment() { 
     c++; 
    } 

    public void decrement() { 
     c--; 
    } 

    public int value() { 
     return c; 
    } 

} 

甲骨文教程提到,如果有两个线程试图访问变量c,它会导致线程干涉,其中一个线程所做的更改不会被另一个线程看到。

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

回答

3

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

  1. Ç从内存
  2. 递增或递减Ç
  3. 写回Ç存储器。

当A和B尝试递增,并在同一时间递减,一个线程可以读取Ç从存储器中(步骤1),而其他线程是在步骤。在这种情况下,如果初始值为c为5,则首先读取线程A并将其增加到6.然后线程B读取并将其减少到4.请记住,B在完成写入之前正在执行这些更改c返回记忆。由于这些步骤是重叠的,因此一个线程所做的更改对另一个线程将不可见,从而导致最终值c6或4。但实际上我们预计的是。

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

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:这是第二edit.The多线程情况,因为这是创造超出这个范围的问题问题我决定删除它。以前我正在创建一个线程数组并运行它们。相反,显示一个执行大量增量的线程和另一个执行大量递减的线程会更好。

我已经使用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 
} 
} 

注意:Synchronized Increment/Decrement方法给出正确的答案。试试看。

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

看一看下面岗位了解更多详情:

Why is synchronized not working properly?