2014-10-08 74 views
4

我有一個關於線程的問題。我有以下Thread類並創建2個線程對象。同步線程方法正在併發執行 - 爲什麼?

public class MyThread extends Thread{ 

    String name=""; 

    public MyThread(String string) { 
     name=string; 
    }  

    @Override 
    public void run() { 
     callMe(); 
    }  

    synchronized private void callMe() { 
     System.out.println("Started"); 
     for (int i = 1; i <= 5; i++) { 
      System.out.println(name+" = "+i); 
     }   
    } 


    public static void main(String[] args) {    
     MyThread a = new MyThread("A"); 
     MyThread b = new MyThread("B"); 

     a.start(); 
     b.start(); 
    }  
} 

當我執行此,輸出我得到的是 -

Started 
Started 
B = 1 
B = 2 
A = 1 
A = 2 
B = 3 
A = 3 
B = 4 
A = 4 
B = 5 
A = 5 

我知道,當線程調度撿起來,A和B是隨機打印。

但我的問題是:爲什麼循環不是一個接一個地執行?我已使用​​關鍵字。

回答

13

你​​方法是有效的:

private void callMe() { 
    synchronized(this) { 
     System.out.println("Started"); 
     for (int i = 1; i <= 5; i++) { 
      System.out.println(name+" = "+i); 
     } 
    } 
} 

現在你已經創建了兩個不同的實例,所以this會爲每個線程的不同...所以他們不同步相互對抗。如果你想看到兩個線程與相同監測工作,你可以重寫你的代碼是這樣的:

public final class DemoRunnable implements Runnable { 
    @Override 
    public synchronized void run() { 
     System.out.println("Started"); 
     for (int i = 1; i <= 5; i++) { 
      System.out.println(Thread.currentThread().getName() + " = " + i); 
     } 
    } 

    public static void main(String[] args) { 
     Runnable runnable = new DemoRunnable(); 
     Thread a = new Thread(runnable, "A"); 
     Thread b = new Thread(runnable, "B"); 
     a.start(); 
     b.start(); 
    } 
} 

然後你會得到的輸出是這樣的:

Started 
A = 1 
A = 2 
A = 3 
A = 4 
A = 5 
Started 
B = 1 
B = 2 
B = 3 
B = 4 
B = 5 

(雖然它可以)

我們仍然有兩個線程,但他們在同一個對象上調用同步方法(在這種情況下爲DemoRunnable),所以必須等待另一個線程完成。

的幾點:

  • 實施Runnable通常優於延伸Thread;它更靈活
  • Thread對象上同步有其自身的問題,因爲Thread類自己做;儘量避免它
  • 我個人不喜歡this同步反正 - 我通常會對Object類型的私人最終變量來表示只有我的代碼知道...這樣,我可以很容易地看到顯示器所有,可以在其上同步的代碼,這使得它更容易推理
+0

因此,在你的第一個代碼片段中,它不會保持Synchronized'method'正確嗎?它變成Synchronized'語句'。第二個代碼片段就是Synchronized'method'的一個例子,對嗎? – 2014-10-08 11:36:52

+0

@NinadPingale:是的,但這種差異實際上是不相關的 - 「同步」方法實際上是一個包含方法體的'synchronized(this)'語句的普通方法。 (例如方法;對於靜態方法,它在'Class'對象上同步。) – 2014-10-08 11:38:16

9

爲每個對象同步的作品。要跨實例進行同步,您需要共享對象才能進行同步。

I.e.

private final static Object globalLock = new Object(); 
// Later 
private void callMe() { 
    synchronized (globalLock) { 
    System.out.println("Started"); 
    for (int i = 1; i <= 5; i++) { 
     System.out.println(name+" = "+i); 
    } 
    } 
} 
+0

globalLock必須強制static..otherwise它doen't工作的權利? – 2014-10-08 11:25:32

+1

使用靜態對象(而不是實例對象)的原因是,每個實例都在同一個對象上進行同步。我建議將'globalLock'聲明爲'private'和'final'。 – Brian 2014-10-08 15:46:24

+0

@Brian Yeah這是一個好主意,從我這裏更新了答案 – jontro 2014-10-08 16:21:10

2

因爲2個線程正在通過2個不同的對象執行。

好吧,您現在必須從其他答案中實現。您正在創建2個獨立的對象,即。 a和b。現在,當您調用start方法時,將啓動一個新線程,通過相應(單獨不相同)對象的run方法執行。同步確實在該方法上,但恰好有一個線程通過a的callMe方法運行,恰好有一個線程通過b的callMe方法運行。所以,你的期望輸出應該是:

A - 1 A - 2 A - 3 ...

沒有發生。

希望這有助於

+0

-1 - 這是一個非常簡潔的答案。沒有足夠的信息來解釋問題發生的原因。 – 2014-10-08 11:02:36

+0

好的,好吧,我會詳細說明:) – Ironluca 2014-10-08 11:04:18

3

代碼等於本守則

private void callMe(){ 
     synchronized(this){ 
      System.out.println("Started"); 
      for (int i = 1; i <= 5; i++) { 
       System.out.println(name+" = "+i); 
      } 
     }       
} 

您只需S與當前對象同步。 如果你想將它們彼此同步您的需要同步到類, 這樣的:

private void callMe(){ 
    synchronized(MyThread.class){ 
    System.out.println("Started"); 
    for (int i = 1; i <= 5; i++) { 
      System.out.println(name+" = "+i); 
    } 
    } 
}