2016-02-28 20 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?