What are the main uses of yield(), and how does it differ from join() and interrupt()?

Viewed 103354

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:

  1. I believe the code below result in the same output both when using yield() and when not using it. Is this correct?

  2. What are, in fact, the main uses of yield()?

  3. In what ways is yield() different from the join() and interrupt() 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
10 Answers

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).

Thread.yield(); frees the bottom thread.

Thread is using OS threads, so Thread.yield(); might free the hardware thread.

Bad implementation for sleep(millis)

public class MySleep {
    public static void sleep(long millis) throws InterruptedException {
        long start = System.currentTimeMillis();
        do {
            Thread.yield();
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        } while (System.currentTimeMillis() - start < millis);
    }
}

and join()

public class MyJoin {
    public static void join(Thread t) throws InterruptedException {
        while (t.getState() != Thread.State.TERMINATED) {
            Thread.yield();
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
    }
    public static void main(String[] args) {
        Thread thread = new Thread(()-> {
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
            }
        });
        thread.start();
        System.out.println("before");
        try {
            join(thread);
        } catch (Exception e) {
        }
        System.out.println("after");
    }
}

This should work even if there is only one hardware thread, unless Thread.yield(); is removed.

Related