2016-11-14 127 views
1

的Java多線程跳過循環,並給出錯誤的結果的Java多線程跳過循環,並給出錯誤的結果

package Threading; 

class DemoThread extends Thread{ //Thread Class 
    static int count=0; // variable incremented by both the threads 

    public DemoThread(String name) { 
     // TODO Auto-generated constructor stub 
     super(name); 
    } 

    public void run() { 
     for(int i=0;i<100000;i++) { 
      count++; 
      System.out.println(Thread.currentThread()+"Count"+count); // print thread operating on count variable 
      try { 
       Thread.sleep(1); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      }   
     }  
    } 
} 


public class MyThreadClass { 

    public static void main(String[] args) { 
     // TODO Auto-generated method stub  
     DemoThread t1=new DemoThread("T1"); 
     DemoThread t2=new DemoThread("T2"); 
     t1.start(); 
     t2.start(); 
     try { 
      t1.join(); 
      t2.join(); //allowing both the threads to complee before main thread 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     System.out.println("Main Thread ends"+DemoThread.count); //final value of count   
    } 
} 

計數的最終值應該是199998,但它沒有給出期望的結果。 爲什麼線程缺少循環?

+0

格式的代碼,請。 –

回答

1

您必須使用java.util.concurrent.atomic.AtomicInteger,而不是共享的靜態int變量。

2

它的發生是因爲線程T1和T2將在同一時間(並行)這樣的更新count

Thread[T1,5,main]Count10 
Thread[T2,5,main]Count10 
Thread[T1,5,main]Count12 
Thread[T2,5,main]Count12 
Thread[T2,5,main]Count14 
Thread[T1,5,main]Count14 
Thread[T1,5,main]Count15 
Thread[T2,5,main]Count16 

您應該使用AtomicInteger

和更新您的代碼:

static int count=0;static AtomicInteger count= new AtomicInteger();

count++;count.incrementAndGet();

+0

但是爲什麼......一次只有1個線程會進入runnable狀態,即獲取CPU權利? –

+0

不,您需要了解有關可運行,線程和並行的更多信息。我認爲 http://www.vogella。com/tutorials/JavaConcurrency/article.html#what-is-concurrency是一篇很好的文章 – Jerry06

+0

它確實有幫助......我明白,它取決於CPU核心,我們可以擁有併發性,因爲幾個線程可能同時訪問共享數據 –

0

問題

在Java中,多線程異步介紹行爲,你的節目,你必須在你需要它執行同步。

沒有同步,沒有任何東西可以阻止一個線程通過調用同一個方法在同一個對象上同時訪問。這被稱爲比賽條件,因爲線程正在彼此競賽以完成該方法。

輸出到你的程序:
enter image description here

輸出的第一行印刷2!?這是因爲t1寫出了count的值,並在打印之前被搶佔。請注意,對於要被搶佔的線程,它不需要進入睡眠狀態。操作系統做這個neverthless。

如果您注意到1 st行和4 th行,您可以看到不一致。這種不一致在巨大的程序中變得不可預測。

以下是多次運行的最終結果。

enter image description here

enter image description here

它不應被視爲理所當然的永遠是錯的結果產生。不,有時會產生正確的結果。這意味着結果將是不可預測的。


誤解?

線程跳過循環。不,線程沒有跳過循環。不同的線程訪問相同的值,但在寫入自己的值之前,其他線程寫入值並繼續。結果下一次訪問該值時,它會得到錯誤的值。

這被稱爲Reader Writer Problem


在程序中,T1T2與不同值的訪問自己計數變量。爲防止其他線程在其他線程完成之前調用run(),我們在方法之前添加了一個同步的關鍵字。

synchronized public void run(){} 

同步關鍵字從比賽條件守衛的狀態。一旦一個線程進入同步方法,其他線程就不能使用該方法,直到前一個線程退出該方法。

以下是同步關鍵字的正確輸出。 注:我用200作爲循環的結束。

enter image description here

enter image description here


加成

如果有正在使用的需要在共享數據作爲輸入專有方法。可以使用同步

synchronized(objRef){ 
    //Method calling 
} 

坷Ref是正在同步的對象的引用。


作爲推薦意見
[推薦的解決方案]

您應該使用的,而不是原生INT的AtomicInteger。使用java.util.concurrent.atomic包。

Reference to java.util.concurrent.atomic

代替static int count = 0;使用static AtomicInteger count = new AtomicInteger(0);和代替count++;使用count.incrementAndGet();

的AtomicInteger固有地同步的。

Atomic Integer Reference Java Docs


+2

詳細,但很好解釋。然而,結論應該是使用AtomicInteger(它完全用於OP的目的)而不是霰彈槍同步。 –

+0

@SME_Dev我按照建議添加了AtomicInteger。 –

+0

@SME_Dev當然,一個更好的解決方案是讓兩個線程都有自己的計數並將它們加在一起,消除運行期間所有同步的需要 – bowmore