2016-04-22 134 views
2

我創建了一個小的虛擬程序,測試Java線程調度:Java線程調度:比處理器更忙的等待線程?

@Test 
    public void testThreadScheduling() throws Exception { 
    int nprocs = Runtime.getRuntime().availableProcessors(); 
    System.out.println(String.format("I have %s processors available", nprocs)); 

    // schedule more threads than I have processors 
    for (int i = 0; i < nprocs + 5; i++) { 
     final int thread = i; 
     new Thread((() -> { 
     System.out.println(String.format("Thread %s has been scheduled", thread)); 
     while (true) { /* busy wait */ } 
     })).start(); 

     // wait a little before spawning the next thread 
     Thread.sleep(100); 
    } 
    } 

現在的輸出,這是(我每次運行它)如出一轍:

I have 12 processors available 
Thread 0 has been scheduled 
Thread 1 has been scheduled 
Thread 2 has been scheduled 
Thread 3 has been scheduled 
... 
Thread 15 has been scheduled 
Thread 16 has been scheduled 

我圖說有可能這是因爲操作系統(或JVM)搶佔已經超過量子的線程,但是,我的問題是,它使用了什麼策略,並且它是正在搶佔的操作系統還是它JVM?

關於發動機罩下面會發生什麼的更多細節將特別感謝!


Java版本 「1.8.0_40」
的Java(TM)SE運行時環境(建立1.8.0_40-B26)
爪哇熱點(TM)64位服務器VM(建立25.40-B25,混合模式)

回答

2

這絕對是操作系統處理線程調度的工作。調度如何實現取決於O/S,在某些情況下,如果O/S允許用戶爲線程優先級設置標誌,則由用戶決定。

如果您有興趣量化這種情況如何發生,您可以測量for循環結束時的實際時間,並查看在第12個線程啓動後,每個迭代是否比100 ms睡眠時間長得多。另一種選擇是使用下面的代碼片段抓住你的JVM的ThreadMXBean的實例,並細讀其線程爭統計:

ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 
threadMXBean.setThreadContentionMonitoringEnabled(true); 

//Then, use specific thread IDs to get contention info periodically: 

int threadID = ... //you can use getId() on each thread you created 
ThreadInfo info = threadMXBean.getThreadInfo(threadID); 
info.getBlockedTime(); //returns millis 
info.getWaitedTime(); //returns millis 

如果你跟蹤你的線程的啓動時間在時代米利斯,您可以結合使用這些最後兩種方法可以知道線程花在CPU上的實際運行時間與等待其他線程的時間。我認爲這個ThreadInfo對象提供的度量與使用​​關鍵字和Java Lock對象嚴格相關,但是,如果您的線程以其他方式解除阻止,由操作系統搶佔。您可以計算總的非等待/非阻塞時間,並將其與實際可用的線程運行時間進行比較,並且差異會讓您的線程被您的O/S預佔的總時間。

我懷疑這可能不會給你你正在尋找的細節的水平,但我認爲這是關於儘可能接近你可以得到沒有寫一堆原生的O/S特定的代碼,並使用JNI在您的Java程序中訪問它。

+0

良好的通話時間測量計劃所需的時間:在線程11之後,它從大約1ms跳到~15ms,然後隨着線程數量的增加而繼續上升! – Almog

+0

聽起來就像調度時間明顯增加。然而,其中一些時間可能會用於創建線程本身 - 但您可以考慮在當前for循環之外初始化所有線程,並且僅在循環中調用「start」方法。這樣你就知道你的測量不受構造函數調用的影響。 – CodeBlind