2016-04-22 86 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