What are the main uses of yield(), and how does it differ from join() and interrupt()?
I am a little bit confused about the use of Thread.yield()
method in Java, specifically in the example code below. I've also read that yield() is 'used to prevent execution of a thread'.
My questions are:
-
I believe the code below result in the same output both when using
yield()
and when not using it. Is this correct? -
What are, in fact, the main uses of
yield()
? -
In what ways is
yield()
different from thejoin()
andinterrupt()
methods?
The code example:
public class MyRunnable implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
for(int i=0; i<5; i++) {
System.out.println("Inside main");
}
}
public void run() {
for(int i=0; i<5; i++) {
System.out.println("Inside run");
Thread.yield();
}
}
}
I obtain the same output using the code above both with and without using yield()
:
Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
Solution 1:
Source: http://www.javamex.com/tutorials/threads/yield.shtml
Windows
In the Hotspot implementation, the way that
Thread.yield()
works has changed between Java 5 and Java 6.In Java 5,
Thread.yield()
calls the Windows API callSleep(0)
. This has the special effect of clearing the current thread's quantum and putting it to the end of the queue for its priority level. In other words, all runnable threads of the same priority (and those of greater priority) will get a chance to run before the yielded thread is next given CPU time. When it is eventually re-scheduled, it will come back with a full full quantum, but doesn't "carry over" any of the remaining quantum from the time of yielding. This behaviour is a little different from a non-zero sleep where the sleeping thread generally loses 1 quantum value (in effect, 1/3 of a 10 or 15ms tick).In Java 6, this behaviour was changed. The Hotspot VM now implements
Thread.yield()
using the WindowsSwitchToThread()
API call. This call makes the current thread give up its current timeslice, but not its entire quantum. This means that depending on the priorities of other threads, the yielding thread can be scheduled back in one interrupt period later. (See the section on thread scheduling for more information on timeslices.)Linux
Under Linux, Hotspot simply calls
sched_yield()
. The consequences of this call are a little different, and possibly more severe than under Windows:
- a yielded thread will not get another slice of CPU until all other threads have had a slice of CPU;
- (at least in kernel 2.6.8 onwards), the fact that the thread has yielded is implicitly taken into account by the scheduler's heuristics on its recent CPU allocation— thus, implicitly, a thread that has yielded could be given more CPU when scheduled in the future.
(See the section on thread scheduling for more details on priorities and scheduling algorithms.)
When to use
yield()
?I would say practically never. Its behaviour isn't standardly defined and there are generally better ways to perform the tasks that you might want to perform with yield():
- if you're trying to use only a portion of the CPU, you can do this in a more controllable way by estimating how much CPU the thread has used in its last chunk of processing, then sleeping for some amount of time to compensate: see the sleep() method;
- if you're waiting for a process or resource to complete or become available, there are more efficient ways to accomplish this, such as by using join() to wait for another thread to complete, using the wait/notify mechanism to allow one thread to signal to another that a task is complete, or ideally by using one of the Java 5 concurrency constructs such as a Semaphore or blocking queue.
Solution 2:
I see the question has been reactivated with a bounty, now asking what the practical uses for yield
are. I'll give an example from my experience.
As we know, yield
forces the calling thread to give up the processor that it's running on so that another thread can be scheduled to run. This is useful when the current thread has finished its work for now but wants to quickly return to the front of the queue and check whether some condition has changed. How is this different from a condition variable? yield
enables the thread to return much quicker to a running state. When waiting on a condition variable the thread is suspended and needs to wait for a different thread to signal that it should continue. yield
basically says "allow a different thread to run, but allow me to get back to work very soon as I expect something to change in my state very very quickly". This hints towards busy spinning, where a condition can change rapidly but suspending the thread would incur a large performance hit.
But enough babbling, here's a concrete example: the wavefront parallel pattern. A basic instance of this problem is computing the individual "islands" of 1s in a bidimensional array filled with 0s and 1s. An "island" is a group of cells that are adjacent to eachother either vertically or horizontally:
1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1
Here we have two islands of 1s: top-left and bottom-right.
A simple solution is to make a first pass over the entire array and replace the 1 values with an incrementing counter such that by the end each 1 was replaced with its sequence number in row major order:
1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8
In the next step, each value is replaced by the minimum between itself and its neighbours' values:
1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4
We can now easily determine that we have two islands.
The part we want to run in parallel is the the step where we compute the minimums. Without going into too much detail, each thread gets rows in an interleaved manner and relies on the values computed by the thread processing the row above. Thus, each thread needs to slightly lag behind the thread processing the previous line, but must also keep up within reasonable time. More details and an implementation are presented by myself in this document. Note the usage of sleep(0)
which is more or less the C equivalent of yield
.
In this case yield
was used in order to force each thread in turn to pause, but since the thread processing the adjacent row would advance very quickly in the meantime, a condition variable would prove a disastrous choice.
As you can see, yield
is quite a fine-grain optimization. Using it in the wrong place e.g. waiting on a condition that changes seldomly, will cause excessive use of the CPU.
Sorry for the long babble, hope I made myself clear.
Solution 3:
About the differences between yield()
, interrupt()
and join()
- in general, not just in Java:
- yielding: Literally, to 'yield' means to let go, to give up, to surrender. A yielding thread tells the operating system (or the virtual machine, or what not) it's willing to let other threads be scheduled in its stead. This indicates it's not doing something too critical. It's only a hint, though, and not guaranteed to have any effect.
- joining: When multiple threads 'join' on some handle, or token, or entity, all of them wait until all other relevant threads have completed execution (entirely or upto their own corresponding join). That means a bunch of threads have all completed their tasks. Then each one of these threads can be scheduled to continue other work, being able to assume all those tasks are indeed complete. (Not to be confused with SQL Joins!)
- interruption: Used by one thread to 'poke' another thread which is sleeping, or waiting, or joining - so that it is scheduled to continue running again, perhaps with an indication it has been interrupted. (Not to be confused with hardware interrupts!)
For Java specifically, see
-
Joining:
How to use Thread.join? (here on StackOverflow)
When to join threads?
Yielding:
-
Interrupting:
Is Thread.interrupt() evil? (here on StackOverflow)
Solution 4:
First, the actual description is
Causes the currently executing thread object to temporarily pause and allow other threads to execute.
Now, it is very likely that your main thread will execute the loop five times before the run
method of the new thread is being executed, so all the calls to yield
will happen only after the loop in the main thread is executed.
join
will stop the current thread until the thread being called with join()
is done executing.
interrupt
will interrupt the thread it is being called on, causing InterruptedException.
yield
allows a context switch to other threads, so this thread will not consume the entire CPU usage of the process.
Solution 5:
The current answer(s) are out-of-date and require revision given recent changes.
There is no practical difference of Thread.yield()
between Java versions since 6 to 9.
TL;DR;
Conclusions based on OpenJDK source code (http://hg.openjdk.java.net/).
If not to take into account HotSpot support of USDT probes (system tracing information is described in dtrace guide) and JVM property ConvertYieldToSleep
then source code of yield()
is almost the same. See explanation below.
Java 9:
Thread.yield()
calls OS-specific method os::naked_yield()
:
On Linux:
void os::naked_yield() {
sched_yield();
}
On Windows:
void os::naked_yield() {
SwitchToThread();
}
Java 8 and earlier:
Thread.yield()
calls OS-specific method os::yield()
:
On Linux:
void os::yield() {
sched_yield();
}
On Windows:
void os::yield() { os::NakedYield(); }
As you can see, Thread.yeald()
on Linux is identical for all Java versions.
Let's see Windows's os::NakedYield()
from JDK 8:
os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
if (os::Kernel32Dll::SwitchToThreadAvailable()) {
return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep(0);
}
return os::YIELD_UNKNOWN ;
}
The difference between Java 9 and Java 8 in the additional check of the existence of the Win32 API's SwitchToThread()
method. The same code is present for Java 6.
Source code of os::NakedYield()
in JDK 7 is slightly different but it has the same behavior:
os::YieldResult os::NakedYield() {
// Use either SwitchToThread() or Sleep(0)
// Consider passing back the return value from SwitchToThread().
// We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
// In that case we revert to Sleep(0).
static volatile STTSignature stt = (STTSignature) 1 ;
if (stt == ((STTSignature) 1)) {
stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
// It's OK if threads race during initialization as the operation above is idempotent.
}
if (stt != NULL) {
return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
} else {
Sleep (0) ;
}
return os::YIELD_UNKNOWN ;
}
The additional check has been dropped due to SwitchToThread()
method are available since Windows XP and Windows Server 2003 (see msdn notes).