The number of CPUs doesn't matter when you're trying to reason about code on this level. In theory you could run the JVM on an OS that forces a context switch after every single program instruction. It would be mad and no OS does that, but how would you know just by looking at the Java code?
If it's a single thread executor, the answer is there will be no overlapping, and if it isn't a single thread executor, you can't prove there will be no overlapping.
To find the reason, we need to look at Chapter 17 of the JLS:
Two actions can be ordered by a happens-before relationship. If one
action happens-before another, then the first is visible to and
ordered before the second.
If we have two actions x and y, we write hb(x, y) to indicate that x
happens-before y.
If x and y are actions of the same thread and x comes before y in
program order, then hb(x, y).
There is a happens-before edge from the end of a constructor of an
object to the start of a finalizer (§12.6) for that object.
If an action x synchronizes-with a following action y, then we also
have hb(x, y).
If hb(x, y) and hb(y, z), then hb(x, z).
The wait methods of class Object (§17.2.1) have lock and unlock
actions associated with them; their happens-before relationships are
defined by these associated actions.
It should be noted that the presence of a happens-before relationship
between two actions does not necessarily imply that they have to take
place in that order in an implementation. If the reordering produces
results consistent with a legal execution, it is not illegal.
In the single thread executor case this is exactly what we get: the two runnables are actions of the same thread and one will be ahead of the other in program order. And though the last paragraph would allow reordering, reordering can't lead to visible differences in correctly synchronised code.
With multiple threads it's anybody's guess. There are only two things that are guaranteed:
- The first message from a thread will be printed before the second. (See above)
- And each line of the output will contain a full message from one of the threads, i.e. you'll never see a line of jumbled output. This is simply because
PrintStream.println()
is synchronised.
So that's the theory.
In practice, with most JVM implementations and operating systems, you'll probably never see an overlap with this exact code for the simple reason that the tasks you execute would take too little time to be interrupted. Make them run for minutes or hours though and you'll definitely see context switching between the two.