Reputation: 741
I created a small dummy program that tests Java Thread scheduling:
@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);
}
}
Now the output to this is (every single time I run it) exactly the same:
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
I figure that the reason that it's possible that this even happens is because the OS (or JVM) is preempting threads that have run past their quanta, my question, however, is what policy does it use and is it the OS that's doing the preempting or is it the JVM?
Any more details about what could be going on underneath the hood would be especially appreciated!
java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
Upvotes: 2
Views: 362
Reputation: 4569
It's definitely the Operating System's job to handle thread scheduling. How scheduling is actually accomplished is up to the O/S, and in some cases, up to the user if the O/S allows users to set flags for thread priority.
If you're interested in quantifying how this happens, you could measure the actual time elapsed at the end of your for-loop and see if each iteration is waiting significantly longer than your 100 ms sleep time after the 12th thread starts. Another option could be to use the following code snippet to grab your JVM's ThreadMXBean instance and peruse its thread contention stats:
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
If you keep track of your thread's startup time in epoch millis, you can use it in conjunction with these last two methods to know how long the thread spent actually running on the CPU vs waiting for other threads. I think the measurements provided by this ThreadInfo
object are strictly tied to using the synchronized
keyword and Java Lock
objects, however, so it may not be an accurate assessment of what your O/S is doing if your threads are otherwise unblocked but are pre-empted by the OS. You could calculate the total non-waiting/non-blocking time and compare that to how much time was actually available for your threads to run, and the difference would give you the total time your threads were pre-empted by your O/S.
I suspect this probably doesn't give you the level of detail you're looking for, but I think this is about as close as you can get without writing a bunch of native, O/S-specific code and using JNI to access it within your Java program.
Upvotes: 2