2013-12-13 50 views
2

我正在通過一本關於線程/同步的簡單示例,聲明使用​​將允許通過在同一實例上調用一個線程來訪問該方法。它按照承諾序列化,但似乎在第二個之前,下面的Synchmain方法中創建的第三個Caller約9/10倍。此代碼是顯示沒有同步方法的問題的示例代碼。爲什麼不是主序列中的呼叫?

class CallMe { 
    void call(String msg) { 
     System.out.print("[" + msg); 
     try { 
      Thread.sleep(1000); 
     } catch (InterruptedException e) { 
      System.out.println("CallMe Interrupted"); 
     } 
     System.out.println("]"); 
    } 
} 

class Caller implements Runnable { 
    String msg; 
    CallMe target; 
    Thread t; 

    public Caller (CallMe target, String msg) { 
     this.target = target; 
     this.msg = msg; 
     t = new Thread(this); 
     t.start(); 
    } 

    @Override 
    public void run() { 
     target.call(msg); 
    } 
} 

class Synch { 
    public static void main(String args[]) { 
     CallMe target = new CallMe(); 
     Caller c1 = new Caller(target, "Hello"); 
     Caller c2 = new Caller(target, "Synchronized"); 
     Caller c3 = new Caller(target, "World"); 

     try {  
      c1.t.join(); 
      c2.t.join(); 
      c3.t.join();   
     } catch (InterruptedException e) { 
      System.out.println("Synch Interrupted"); 
     } 
    } 
} 

該書顯示了兩種方式來處理這個問題,他們是 -
synchronized void call(String msg) {...}
public void run() { synchronized (target) {...} }

很顯然,這兩種選擇工作,因爲,相對於原來的代碼,括號內的字是一致喜歡...

[你好]
【世界】(時間約90%的電話是向後)
[同步](1 /許多人同步爲第一MSG)

...有沒有無緣無故的原代碼。所以我知道它是「工作的」,並且可以通過在每個Caller實例化中放置斷點來直接看到它。它每次都有效,當我這樣做的時候,對我來說很明顯。

爲什麼第三個Caller在第二個之前一直呼叫call

回答

4

根據定義,線程並行運行,沒有優先於任何其他優先。

一旦線程都推出了它本質上是隨機的,這將首先運行,一般第一個推出都會有輕微的「先聲奪人」,但先聲奪人相比發起線程的開銷等微小

您的特定環境的一個怪癖恰恰是傾向於一個線程,結果可能在不同的系統上有所不同,當然不應該依賴。

順便說一句這是有很多原因不好的做法:

public Caller (CallMe target, String msg) { 
    this.target = target; 
    this.msg = msg; 
    t = new Thread(this); 
    t.start(); 
} 

(你可能得到實際上是一個編譯器警告)。

好多是提供一個start方法

public Caller start() { 
    t.start(); 
    return this; 
} 

,然後做

new Caller(target, msg).start(); 

這絕對保證了來電對象被完全初始化並準備去線程開始處理之前。

+0

謝謝,我會接受你的建議。我認爲這個示例代碼只是爲了演示,我不認爲這不是一個非常有用的異常處理。 – ChiefTwoPencils

+0

如果你對此感到滿意,你能否接受答案:) –

+0

當然,你能告訴我應該得到什麼樣的編譯器警告,我沒有得到嗎? – ChiefTwoPencils

4

爲什麼第三次呼叫在第二次呼叫之前一直呼叫?

這並不是一直如此 - 大約有90%的時間這樣做。

基本上,同步並不保證是先入先出......並且不能保證這些調用甚至可以按您期望的順序進行。三個新線程正在快速連續開始 - 有沒有保證哪個線程將首先實際開始執行其代碼。

基本上,如果你想對並行代碼強加排序,你需要明確地這樣做。同步不提供排序 - 它只提供排他性。

+0

謝謝,對我好奇的是,爲什麼[書](http://www.barnesandnoble.com/listing/2682716016759?r=1&cm_mmca2=pla&cm_mmc=GooglePLA-_-TextBook_NotInStock_26To75-_-Q000000633-_-2682716016759),使得這聽起來很重要嗎? – ChiefTwoPencils

+0

@BobbyDigital:這本書講的究竟是什麼?它是否說我們一定會以可預測的順序獲得結果,還是隻是說我們不會得到交錯輸出?這些是非常不同的東西。 –

+0

「要修復前面的程序,你必須......做到這一點,你只需要在'call()'的定義之前......」。你有一個很好的觀點,他們不是太具體,但是會說「程序的輸出*是* ...」並且使用了「正確的」輸出,但是並不保證我想的任何東西。 – ChiefTwoPencils

1

它按照承諾進行序列化,但似乎在第二個同步主方法中創建的第三個調用者的約9/10倍。

要認真瞭解在你的句子「連載」的意義:它意味着所有由同一個鎖保護的代碼段將永遠並行運行;換句話說,他們的執行將是serial

它是什麼不是的意思是「這些代碼段的執行將以嚴格的指定順序進行」。它不會。

相關問題