Java Concurrency – Difference between yield() and join()

Multi-threading is very popular topic among interviewers from long time. Though I personally feel that very few of us get real chance to work on a complex multi-threaded application (I got only one chance in last 7 years), still it helps in having the concepts handy to boost your confidence ONLY. Previously, I discussed a similar question about difference between wait() and sleep() method, this time I am discussing difference between join() and yield() methods. Frankly speaking, I have not used any of both methods in practical so please make a argument if you feel otherwise at any point.

A little background on java thread scheduling

A Java virtual machine is required to implement a preemptive, priority-based scheduler among its various threads. This means that each thread in a Java program is assigned a certain priority, a positive integer that falls within a well-defined range. This priority can be changed by the developer. The Java virtual machine never changes the priority of a thread, even if the thread has been running for a certain period of time.

The priority value is important because the contract between the Java virtual machine and the underlying operating system is that the operating system must generally choose to run the Java thread with the highest priority. That’s what we mean when we say that Java implements a priority-based scheduler. This scheduler is implemented in a preemptive fashion, meaning that when a higher-priority thread comes along, that thread interrupts (preempts) whatever lower-priority thread is running at the time. The contract with the operating system, however, is not absolute, which means that the operating system can sometimes choose to run a lower-priority thread. [I hate this about multi-threading.. nothing is guaranteed :-( ]

Also note that java does not mandate that its threads be time-sliced, but most operating systems do so. There is often some confusion in terminology here: preemption is often confused with time-slicing. In fact, preemption means only that a higher-priority thread runs instead of a lower-priority one, but when threads have the same priority, they do not preempt each other. They are typically subject to time-slicing, but that is not a requirement of Java.

Understanding thread priorities

Understanding the Thread priorities is next important step in learning Multi-threading and specially how yield() works.

  1. Remember that all the threads carry normal priority when a priority is not specified.
  2. Priorities can be specified from 1 to 10. 10 being the highest, 1 being the lowest priority and 5 being the normal priority.
  3. Remember that the thread with highest priority will be given preference in execution. But there is no guarantee that it will be in running state the moment it starts.
  4. Always the currently executing thread might have the higher priority when compared to the threads in the pool who are waiting for their chance.
  5. It is the thread scheduler which decides what thread should be executed.
  6. t.setPriority() can be used to set the priorities for the threads.
  7. Remember that the priorities should be set before the threads start method is invoked.
  8. You can use the constants, MIN_PRIORITY,MAX_PRIORITY and NORM_PRIORITY for setting priorities.

Now when we have some basic understanding of thread scheduling and thread priorities, let’s jump into subject.

yield() method

Theoretically, to ‘yield’ means to let go, to give up, to surrender. A yielding thread tells the virtual machine that it’s willing to let other threads be scheduled in its place. This indicates that it’s not doing something too critical. Note that it’s only a hint, though, and not guaranteed to have any effect at all.

yield() is defined as following in Thread.java.

/**
  *	A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore
  * this hint. Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilize a CPU. 
  * Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect. 
  */

public static native void yield();

Let’s list down important points from above definition:

  • Yield is a Static method and Native too.
  • Yield tells the currently executing thread to give a chance to the threads that have equal priority in the Thread Pool.
  • There is no guarantee that Yield will make the currently executing thread to runnable state immediately.
  • It can only make a thread from Running State to Runnable State, not in wait or blocked state.

yield() method example usage

In below example program, I have created two threads named producer and consumer for no specific reason. Producer is set to minimum priority and consumer is set to maximum priority. I will run below code with/without commenting the line Thread.yield(). Without yield(), though the output changes sometimes, but usually first all consumer lines are printed and then all producer lines.

With using yield() method, both prints one line at a time and pass the chance to another thread, almost all the time.

package test.core.threads;

public class YieldExample
{
   public static void main(String[] args)
   {
      Thread producer = new Producer();
      Thread consumer = new Consumer();
      
      producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
      consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
      
      producer.start();
      consumer.start();
   }
}

class Producer extends Thread
{
   public void run()
   {
      for (int i = 0; i < 5; i++)
      {
         System.out.println("I am Producer : Produced Item " + i);
         Thread.yield();
      }
   }
}

class Consumer extends Thread
{
   public void run()
   {
      for (int i = 0; i < 5; i++)
      {
         System.out.println("I am Consumer : Consumed Item " + i);
         Thread.yield();
      }
   }
}

Output of above program “without” yield() method

I am Consumer : Consumed Item 0
 I am Consumer : Consumed Item 1
 I am Consumer : Consumed Item 2
 I am Consumer : Consumed Item 3
 I am Consumer : Consumed Item 4
 I am Producer : Produced Item 0
 I am Producer : Produced Item 1
 I am Producer : Produced Item 2
 I am Producer : Produced Item 3
 I am Producer : Produced Item 4

Output of above program “with” yield() method added

I am Producer : Produced Item 0
 I am Consumer : Consumed Item 0
 I am Producer : Produced Item 1
 I am Consumer : Consumed Item 1
 I am Producer : Produced Item 2
 I am Consumer : Consumed Item 2
 I am Producer : Produced Item 3
 I am Consumer : Consumed Item 3
 I am Producer : Produced Item 4
 I am Consumer : Consumed Item 4

join() method

The join() method of a Thread instance can be used to “join” the start of a thread’s execution to the end of another thread’s execution so that a thread will not start running until another thread has ended. If join() is called on a Thread instance, the currently running thread will block until the Thread instance has finished executing.

//Waits for this thread to die. 

public final void join() throws InterruptedException

Giving a timeout within join(), will make the join() effect to be nullified after the specific timeout. When the timeout is reached, the main thread and taskThread are equally probable candidates to execute. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.

Like sleep, join responds to an interrupt by exiting with an InterruptedException.

join() method example usage

package test.core.threads;

public class JoinExample
{
   public static void main(String[] args) throws InterruptedException
   {
      Thread t = new Thread(new Runnable()
         {
            public void run()
            {
               System.out.println("First task started");
               System.out.println("Sleeping for 2 seconds");
               try
               {
                  Thread.sleep(2000);
               } catch (InterruptedException e)
               {
                  e.printStackTrace();
               }
               System.out.println("First task completed");
            }
         });
      Thread t1 = new Thread(new Runnable()
         {
            public void run()
            {
               System.out.println("Second task completed");
            }
         });
      t.start(); // Line 15
      t.join(); // Line 16
      t1.start();
   }
}

Output:

First task started
Sleeping for 2 seconds
First task completed
Second task completed

That’s all for this quite small but important concept. Let me know of your thoughts in comments section.

Happy Learning !!

Comments

Subscribe
Notify of
guest
31 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode