2017-09-02 66 views
0

我從擴展Thread類的類A創建了兩個線程a1a2。類A聲明瞭兩個同步的構造函數和run方法。當我以這種形式編寫的代碼兩個線程有​​時開始在同一時間雖然run方法聲明爲synchronized和我得到的結果一樣java中的同步關鍵字

0 0 1 1 2 3 4 5 6 7 2 8 3 9 4 10 5 11 6 12 7 8 9 10 11 12 

代碼:

public class Main { 
    public static void main(String[] args) {  
     A t = new A() ;  
     A a1 = new A (t) ;  
     A a2 = new A (t) ; 

     a1.start(); 
     a2.start(); 
    }  
} 

class A extends Thread { 
    public A() { 
     super() ; 
    } 

    public A(Thread th) { 
     super(th) ; 
    } 
    @Override 
    public synchronized void run() { 
     for (int i = 0; i <13; i++) { 
      System.out.print(i+" ");    
     }  
    } 
} 

但是,當我通過創建Thread類不是A兩個線程,

Thread a1 = new Thread (t); 
Thread a2 = new Thread (t); 

同步方法run工作,兩個線程不會在開始同時總是給結果

0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 

我的問題:爲什麼synchronized關鍵字,當我創建A級兩個線程(雖然我定義了兩個構造函數)不工作,工作時,我創建Thraed類兩個線程?

+2

您如何看到第一種情況下的線程在同一時間開始?你看到混合輸出嗎?你能告訴我們那個輸出嗎? –

+1

我可以向你保證'synchronized'確實能正常工作。你正在同步線程對象本身,這真的有點沒有意義。 –

+0

我的朋友同步始終正常工作。現在你的錯誤是在這裏理解**線程a1 =新線程(t);線程a2 =新的線程(t); **小的描述=在這裏你創建兩個不同的對象有兩個不同的鎖權利?所以爲什麼一個線程會等待你有這個?兩個線程都有不同的鎖定。得到它了? –

回答

1

當您在某些時候調用start()運行的線程將調用其run()方法,在Thread類看起來是這樣的:

public void run() { 
    if (target != null) { 
     target.run(); 
    } 
} 

此代碼是負責從Runnable target對象run方法new Thread(Runnable target)通過執行代碼構造函數。

但是你在A課程中覆蓋了Thread#run()。所以,現在start()方法調用A#run(因爲多態性),這意味着它永遠不會調用target.run()(在你的情況 - t.run()因爲t是爲A線體通過)。
現在,即使A#run方法是​​,因爲每個線程都在單獨的實例(線程對象本身)上調用它,所以不會發生同步,因爲線程不使用常見的鎖定/監視器。

由於有時候一個線程能夠在其他啓動之前完成其全部工作,因此您會獲得正確的結果。


爲了避免這樣的混亂問題(和許多其他)在所有沒有擴展Thread。創建實現Runnable的類並將其傳遞給Thread實例。

想想Runnable任務,並Thread工人應該執行該任務。你的工作是描述什麼工人應該做的(拿這個,把它放在那裏),而不是如何(彎曲你的膝蓋,抓住,..)。

+0

非常感謝您的回答 –

0

除非使用同步器,如Semaphore,CountDownLatchCyclicBarrier,否則無法確定線程是否同時啓動。 ​​關鍵字本身用於保護相同的對象(方法或代碼塊)免於併發訪問。在你的代碼​​是無用的。

0

下面是修改後的代碼來處理您的問題。

public class RunnableDemo { 
    public static void main(String[] args) {  

     Object lock = new Object(); 
     Thread t1 = new Thread (new MyRunnable(lock));  
     Thread t2 = new Thread (new MyRunnable(lock));  

     t1.start(); 
     t2.start(); 
    }  
} 

class MyRunnable implements Runnable { 
    Object lock = new Object(); 
    public MyRunnable(Object lock){ 
     this.lock = lock; 
    } 

    @Override 
    public void run() { 
     synchronized(lock){ 
      for (int i = 0; i <13; i++) { 
       System.out.print(i+" ");    
      } 
     }   
    } 
} 
  1. 共享鎖在主類中創建:RunnableDemo
  2. 兩個線程已經通過傳遞MyRunnable對象,它實現接口Runnable創建。代替擴展Thread以創建新的Thread,將Runnable接口實現傳遞給Thread構造函數是首選方法。
  3. 兩個線程都已啓動。因爲他們有着共同的鎖,只有一個Thread完整run()
  4. 現在你可以看到輸出是如下正確的順序:

    0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 
    

我剛剛展示了基本的鎖定機制。對於高級版本的多線程,您可以參考high level concurrency教程。