2011-05-03 33 views
11

我已經實現與JVMTI一個簡單的分析器對wait()notifyAll()顯示的調用。作爲我正在使用的測試用例。 producer consumer example of Oracle。我有以下三個事件:notifyAll的()調用差的數量,同時仿形

  • notifyAll的()被調用
  • wait()的調用
  • 等待()是左

wait()調用,當它離開它由異型使用事件MonitorEnterMonitorExit。當名稱爲notifyAll的方法退出時,將調用notifyAll()調用。

現在我有以下的效果,首先是從探查本身和二是從Java,在那裏我已經把相應的System.out.println聲明。

// Profiler: 
    Thread-1 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 left wait() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 left wait() 
    Thread-1 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 left wait() 
    Thread-1 invoked notifyAll() 

    // Java: 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 invoked notifyAll() 
    Thread-0 invoked notifyAll() 
    Thread-1 invoked wait() 
    Thread-1 invoked notifyAll() 

有人解釋了這種差異來自何處? notifyAll()被稱爲很多次。有人告訴我這可能是由於Java對操作系統的請求產生誤報。

A notifyAll()請求它發送到操作系統併發送誤報響應,看起來請求已成功。由於notifyAll由分析方法調用而不是MonitorEnter記錄,因此可以解釋爲什麼在等待時不會發生這種情況。

我忘了說,我沒有單獨運行程序,兩個日誌都來自同一個執行。

aditional的信息

最初加入作爲一個答案,移至提問extraneon:

我想我找到了在那裏附加notifyAll的一些是從哪裏來的,我加剖析其中notifyAll的調用該方法的上下文:

723519: Thread-1 invoked notifyAll() in Consumer.take 
3763279: Thread-0 invoked notifyAll() in Producer.put 
4799016: Thread-0 invoked notifyAll() in Producer.put 
6744322: Thread-0 invoked notifyAll() in Producer.put 
8450221: Thread-0 invoked notifyAll() in Producer.put 
10108959: Thread-0 invoked notifyAll() in Producer.put 
39278140: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
40725024: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
42003869: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
58448450: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
60236308: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
61601587: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 
70489811: Thread-1 invoked notifyAll() in Consumer.take 
75068409: Thread-1 invoked wait() in Drop.take 
75726202: Thread-1 left wait() in Drop.take 
77035733: Thread-1 invoked notifyAll() in Consumer.take 
81264978: Thread-1 invoked notifyAll() in Consumer.take 
85810491: Thread-1 invoked wait() in Drop.take 
86477385: Thread-1 left wait() in Drop.take 
87775126: Thread-1 invoked notifyAll() in Consumer.take 

但即使沒有這些外部調用,有n的plteny otify所有不在printf調試中顯示的調用。

+0

當你處理w /併發代碼時,使用system.out/err來跟蹤是一個可怕的想法。 Logging/System.out/err只是強加顯式同步+額外的緩存一致性。顯示導致問題的真實代碼,我會看看有什麼可能弄清楚。 – bestsss 2011-05-07 17:50:29

+0

@bestsss我不是在談論顯示的事件順序,只是對notifyAll進行的調用總數。你的論點仍然存在嗎?真正的代碼如上所示,它是Oracle Java教程的一個例子。 – 2011-05-08 07:20:03

+0

由於簡單的原因只有很多通知是正常的,並不是每個'notify'都會喚醒找到處於'wait'狀態的其他線程。添加追蹤可能會使問題惡化。我發現這種行爲完全正常。我使用CAS(比較和設置/交換)來避免輸入同步塊並廣泛地調用通知。 – bestsss 2011-05-08 12:44:46

回答

4

我花了一些時間分析由Oracle和你的輸出(分析器和Java程序)提供的Producer-Consumer例子。有你的輸出一些奇怪的事情,除了幾個意想不到notifyAll()

  1. 我們應該期待的wait()方法來執行4次(String陣列由生產者操縱的有4種元素)。您的配置文件的結果顯示它只執行了三次 。

  2. 另一件奇怪的事情是探查器輸出中線程的編號。該示例有兩個線程,但是您的分析器在一個線程中執行所有代碼,即Thread-1,而Thread-0只是執行notifyAll()

  3. 提供的示例代碼被正確地從一個並行的視角和語言透視編程:wait()notifyAll()是在同步方法,以確保在監視控制;等待條件在while循環內,正確放置在方法的最後。但是,我注意到catch (InterruptedException e)塊是空的,這意味着如果正在等待的線程中斷,將執行notifyAll()方法。這可能是一些意想不到的原因notifyAll()

總之,在不執行代碼的一些修改和執行一些額外的測試,它不會很容易找出其中的問題從何而來。

作爲一個方面說明,我會離開這個鏈接Creating a Debugging and Profiling Agent with JVMTI爲好奇的想與JVMTI玩的那個。

+0

看到一個notifyAll(),你的第二點是什麼意思:使用profiler執行一個線程中的所有代碼?這沒有發生,Java程序作爲任何其他程序啓動,這是注入性能分析代理中唯一的補充。 – 2011-05-03 17:46:13

+0

@platzhirsch在剖析器輸出中,有兩個線程:'Thread-0'和'Thread-1'。但是所有的應用程序輸出都是由'Thread-1'打印的。那麼,我假設這是因爲'Thread-0'不會輸出任何'wait''字符串。 – bacchus 2011-05-03 17:49:50

1

如果您的代碼中存在競爭條件,那麼分析器可能會使代碼變慢,從而無法在代碼中顯示或隱藏錯誤。 (我喜歡在分析器中運行我的程序,只是爲了顯示競爭條件。)

因爲notifyAll()只會通知wait()ing線程,所以在notifyAll()之後調用wait()很可能導致錯過通知。即它的無狀態,它不知道你之前通知過通知。

如果您減慢應用程序的速度,可以延遲notifyAll(),直到wait()開始之後。

+1

@ Peter-Layrey:結果來自同一個執行,這意味着Java輸出也是被注入的探查器代理程序拋棄的代碼。所以這並不能解釋爲什麼notifyAll在開始時經常被調用。 – 2011-05-03 12:43:00

+0

這是真的。我很驚訝這是從第一個「等待」打印出來,但在第二個它不是。你對這有何可能性有任何想法? – 2011-05-03 13:00:02

+0

@ Peter-Lawrey:因爲我忘了將println放在左邊wait(),這是正常的 - 我應該包含它。但重點是通知所有,或者你認爲這與它有關嗎? – 2011-05-03 13:02:58