Java Thread Starvation and Livelock with Examples

Java Thread Starvation

Starvation occurs when a thread is continually denied access to resources and as a result it is unable to make progress. This usually happens when greedy threads consume shared resources for long periods of time. When this happens for extended periods of time, the thread not getting enough CPU time or access to the resource will not be able to make enough progress leading to thread starvation. One of the likely causes of thread starvation is incorrect thread priorities among different threads or thread groups.

Another possible cause could be use of non-terminating loops (infinite loops) or waiting excessive amount of time on specific resources while holding on to critical locks required by other threads.

It is generally recommended to try to stay clear of modifying thread priorities as this is the main culprits of causing thread starvation. Once you start tweaking your application with thread priorities it becomes tightly coupled to the specific platform and you also introduce the risk of thread starvation.

thread_starvation_scenario

Java Thread Starvation Example

In my example, I will creating five threads in total. Each of the threads will be assigned a different thread priority. Once the threads have been created and assigned the priorities, we will go ahead and start all five threads. In the Main Thread we will wait for 5000ms or 5 seconds and change the isActive flag to false so that all thread exit the while loop and go into the dead thread state.

The Worker class which implements Runnable interface is synchronizing on a mutex (Object) to simulate thread lock a critical section of code even though I do use the concurrent class for AtomicInteger which performs a getAndIncrement operation and does not require locking. I am using a counter so that we can count and see how often work has been performed for each worker thread. As a general guideline, the higher priority threads should get more CPU cycles so the values should be larger for the higher priority threads.

Note

Windows implements a thread fallback mechanism whereby a thread that has not had a chance to run for a long time is given a temporary priority boost so complete starvation is next to impossible to achieve. However, from the numbers I generated you can see how the thread priority is having quite a substantial impact on the amount of CPU time being allocated to thread 5.

Thread Starvation Example

package com.avaldes.tutorials;

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadStarvationExample {
  private static Object mutex = new Object();
  private static volatile boolean isActive = true;
  
  public static void main(String[] args) {
    Thread t1 = new Thread(new Worker(), "Thread_1_P10");
    Thread t2 = new Thread(new Worker(), "Thread_2_P8");
    Thread t3 = new Thread(new Worker(), "Thread_3_P6");
    Thread t4 = new Thread(new Worker(), "Thread_4_P4");
    Thread t5 = new Thread(new Worker(), "Thread_5_P2");
    
    // Priorities only serve as hints to scheduler, it is up to OS implementation to decide
    t1.setPriority(10);
    t2.setPriority(8);
    t3.setPriority(6);
    t4.setPriority(4);
    t5.setPriority(2);
    
    t1.start();
    t2.start();
    t3.start();   
    t4.start();   
    t5.start();   
    
    //  Make the Main Thread sleep for 5 seconds
    //  then set isActive to false to stop all threads 
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    isActive = false;
    
  }
  
  private static class Worker implements Runnable {
    private AtomicInteger runCount = new AtomicInteger();
    
    public void run() {
      // tight loop using volatile variable as active flag for proper shutdown
      while (isActive) {
        synchronized (mutex) {
          try {
              doWork();
          } catch (Exception e) {
            System.out.format("%s was interrupted...\n", Thread.currentThread().getName());
            e.printStackTrace();
          }
        }
      }
      System.out.format("DONE===> %s: Current runCount is %d...\n", Thread.currentThread().getName(), runCount.get());
    }
    
    private void doWork() {
      System.out.format("%s: Current runCount is %d...\n", Thread.currentThread().getName(), runCount.getAndIncrement());
    }
  }
}

Output for Java Thread Starvation Example

Thread_2_P8: Current runCount is 30399...
Thread_2_P8: Current runCount is 30400...
Thread_2_P8: Current runCount is 30401...
Thread_2_P8: Current runCount is 30402...
Thread_2_P8: Current runCount is 30403...
Thread_2_P8: Current runCount is 30404...
Thread_2_P8: Current runCount is 30405...
Thread_2_P8: Current runCount is 30406...
DONE===> Thread_2_P8: Current runCount is 30407...
Thread_5_P2: Current runCount is 545...
Thread_1_P10: Current runCount is 40651...
DONE===> Thread_1_P10: Current runCount is 40652...
DONE===> Thread_5_P1: Current runCount is 546...
Thread_4_P4: Current runCount is 10013...
DONE===> Thread_4_P4: Current runCount is 10014...
Thread_3_P6: Current runCount is 64028...
DONE===> Thread_3_P6: Current runCount is 64029...

Count Analysis for Thread Starvation Run Counts

DONE===> Thread_1_P10: Current runCount is 40652...
DONE===> Thread_2_P8: Current runCount is 30407...
DONE===> Thread_3_P6: Current runCount is 64029...
DONE===> Thread_4_P4: Current runCount is 10014...
DONE===> Thread_5_P2: Current runCount is 546...

Java Thread LiveLock Example

Thread liveLock is a condition that closely resembles deadlock in that several processes are blocking each other. But with livelock, a thread is unable to make any progress because every time it tries the operation always fails. Thread livelock can also occur when all threads call Object.wait(). This program will be live-locked and can not proceed until some other thread calls either notify() or notifyAll() but since all other threads have also called wait(), neither call can ever be made.

For detailed examples of using wait(), notify() and notifyAll() please go to my tutorial Java Threads Wait, Notify and NotifyAll Example

Another reason that may cause livelock is when threads take action to respond to each other. If one thread is taking action and responding to the other and the other thread is also taking action to respond to its blocked continue and each action taken cause the condition to wait or block again this, in effect, will cause a condition likened to deadlock as thread will continue reacting but unable to make any progress. The Java documentation gave a nice illustration that I would like to pass on, “Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. Seeing that they are still blocking each other, Alphonse moves to his right, while Gaston moves to his left. They’re still blocking each other, so…”, for full details on this illustration please visit the Java Tutorials.

The final reason, livelock can also occur if all threads are stuck in infinite loops. Since the programs are not able to escape out of this condition this causes a livelock condition.

Java Thread LiveLock Example

package com.avaldes.tutorials;

import java.util.LinkedList;

public class ThreadLiveLockExample {
  public static void main(String[] args) {
    LinkedList<Equation> queue = new LinkedList<Equation>();
    
    Thread t1 = new Thread(new Reader(queue), "Thread_1_P10");
    Thread t2 = new Thread(new Reader(queue), "Thread_2_P10");
    Thread t3 = new Thread(new Reader(queue), "Thread_3_P10");
    Thread t4 = new Thread(new Reader(queue), "Thread_4_P10");
    Thread t5 = new Thread(new Reader(queue), "Thread_5_P1");
    
    t1.start();
    t2.start();
    t3.start();   
    t4.start();   
    t5.start();   
    
    queue.add(new Equation(100,5));
    queue.add(new Equation(120,6));
    queue.add(new Equation(101,3));
    queue.add(new Equation(1024,62));
    queue.add(new Equation(1892090,53));
    queue.add(new Equation(72,8));
    queue.add(new Equation(198,0));   // Will cause Divide by Zero ArithmeticException !!!
    queue.add(new Equation(123,23));
    queue.add(new Equation(98495,876));
    
  }
  
  private static class Reader implements Runnable {
    LinkedList<Equation> queue = null;
    
    public Reader(LinkedList<Equation> queue) {
      this.queue = queue;
    }
    
    public void run() {
      while (true) {
        synchronized (queue) {
          System.out.format("%s Checking elements in the queue...\n", Thread.currentThread().getName());
          try {
            if (queue.size() > 0) {
              Equation eq = queue.remove(0);
              doWork(eq);
              queue.wait(200);
            }
            Thread.sleep(1000);
            queue.notify();
          } catch (InterruptedException e) {
            System.out.format("%s was interrupted...\n", Thread.currentThread().getName());
            e.printStackTrace();
          }
        }
      }
    }
    
    private void doWork(Equation eq) {
      double val = 0;
      
      try {
        val = (eq.getDividend() / eq.getDivisor());
        System.out.format("%s: Equation %d / %d = %f\n", Thread.currentThread().getName(), eq.getDividend(), eq.getDivisor(), val);
      } catch (ArithmeticException ex) {
        ex.printStackTrace();
        // Try to recover from error --- Incorrect Logic
        // put equation back into queue as the first element
        queue.addFirst(eq);
      }
    }
  }
  
  private static class Equation {
    private int dividend;
    private int divisor;
    
    public Equation(int dividend, int divisor) {
      setDividend(dividend);
      setDivisor(divisor);
    }
    
    public int getDividend() {
      return dividend;
    }
    
    public void setDividend(int dividend) {
      this.dividend = dividend;
    }
    
    public int getDivisor() {
      return divisor;
    }
    
    public void setDivisor(int divisor) {
      this.divisor = divisor;
    }
    
  }
}

Output for Java Thread LiveLock Example

As you can see from the output below, because of the incorrect logic we used in the application we have created a livelock situation as we continue to put the equation that generates the exception back into the queue as the first element. From this point on, every thread will fail with the same error — LIVELOCK

Thread_1_P10 Checking elements in the queue...
Thread_1_P10: Equation 100 / 5 = 20.000000
Thread_5_P1 Checking elements in the queue...
Thread_5_P1: Equation 120 / 6 = 20.000000
Thread_4_P10 Checking elements in the queue...
Thread_4_P10: Equation 101 / 3 = 33.000000
Thread_3_P10 Checking elements in the queue...
Thread_3_P10: Equation 1024 / 62 = 16.000000
Thread_2_P10 Checking elements in the queue...
Thread_2_P10: Equation 1892090 / 53 = 35699.000000
Thread_1_P10 Checking elements in the queue...
Thread_1_P10: Equation 72 / 8 = 9.000000
Thread_2_P10 Checking elements in the queue...
java.lang.ArithmeticException: / by zero
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.doWork(ThreadLiveLockExample.java:70)
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.run(ThreadLiveLockExample.java:53)
	at java.lang.Thread.run(Thread.java:662)
Thread_3_P10 Checking elements in the queue...
java.lang.ArithmeticException: / by zero
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.doWork(ThreadLiveLockExample.java:70)
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.run(ThreadLiveLockExample.java:53)
	at java.lang.Thread.run(Thread.java:662)
Thread_5_P1 Checking elements in the queue...
java.lang.ArithmeticException: / by zero
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.doWork(ThreadLiveLockExample.java:70)
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.run(ThreadLiveLockExample.java:53)
	at java.lang.Thread.run(Thread.java:662)
Thread_4_P10 Checking elements in the queue...
java.lang.ArithmeticException: / by zero
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.doWork(ThreadLiveLockExample.java:70)
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.run(ThreadLiveLockExample.java:53)
	at java.lang.Thread.run(Thread.java:662)
Thread_5_P1 Checking elements in the queue...
java.lang.ArithmeticException: / by zero
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.doWork(ThreadLiveLockExample.java:70)
	at com.avaldes.tutorials.ThreadLiveLockExample$Worker.run(ThreadLiveLockExample.java:53)
	at java.lang.Thread.run(Thread.java:662)

Related Posts

  • Java Thread, Concurrency and Multithreading Tutorial
    This Java Thread tutorial will give you a basic overview on Java Threads and introduce the entire tutorial series on concurrency and multithreading. From here, you will learn about many java thread concepts like: Thread States, Thread Priority, Thread Join, and ThreadGroups. In addition, you will learn about using the volatile keyword and examples on using wait, notify and notifyAll.
  • Java Thread States - Life Cycle of Java Threads
    Get a basic understanding of the various thread states. Using the state transition diagram we show the various states for a Java thread and the events that cause the thread to jump from one state to another.
  • Creating Java Threads Example
    In this post we cover creating Java Threads using the two mechanisms provided in Java, that is, by extending the Thread class and by implementing Runnable interface for concurrent programming.
  • Java Thread Priority Example
    In this post we cover Thread priorities in Java. By default, a java thread inherits the priority (implicit) of its parent thread. Using the setPriority() method you can increase or decrease the thread priority of any java thread.
  • Java ThreadGroup Example
    Sometimes we will need to organize and group our threads into logical groupings to aid in thread management. By placing threads in a threadGroup all threads in that group can be assigned properties as a set, instead of going through the tedious task of assigning properties individually.
  • Java Thread Sleep Example
    We seem to use this method very often to temporarily suspend the current threads execution for a specific period of time. Let's spend some time and familiarize ourselves with what this method actually does.
  • Java Thread Join Example
    In Java, using Thread.join() causes the current thread to wait until the specified thread dies. Using this method allows us to impose an order such that we can make one thread wait until the other completes doing what it needed to do, such as completing a calculation.
  • Examining Volatile Keyword with Java Threads
    When we declare a field as volatile, the JVM will guarantee visibility, atomicity and ordering of the variable. Without it the data may be cached locally in CPU cache and as a result changes to the variable by another thread may not be seen by all other threads resulting in inconsistent behaviour.
  • Java Threads Wait, Notify and NotifyAll Example
    The purpose of using notify() and notifyAll() is to enable threads to communicate with one another via some object on which to performing the locking. A thread using the wait() method must own a lock on the object. Once wait() is called, the thread releases the lock, and waits for another thread to either call notify() or notifyAll() method.
  • Java Thread Deadlock Example and Thread Dump Analysis using VisualVM
    Deadlock is a condition where several threads are blocking forever, waiting for the other to finish but they never do. This tutorial will discuss situations that will lead to Java Thread deadlock conditions and how they can be avoided. In addition, we will discuss using Java VisualVM to pinpoint and analyze the source of the deadlock conditions.
  • Java Thread Starvation and Livelock with Examples
    Starvation occurs when a thread is continually denied access to resources and as a result it is unable to make progress. Thread liveLock is a condition that closely resembles deadlock in that several processes are blocking each other. But with livelock, a thread is unable to make any progress because every time it tries the operation always fails.
  • Java Synchronization and Thread Safety Tutorial with Examples
    One of Java's many strengths come from the fact that it supports multithreading by default as has so from the very onset. One of the mechanisms that Java uses for this is via synchronization. When we use the synchronized keyword in Java we are trying limit the number of threads that can simultaneously access and modify a shared resource. The mechanism that is used in Java's synchronization is called a monitor.
  • Creating a Thread Safe Singleton Class with Examples
    In this tutorial we cover many examples of creating thread-safe singleton classes and discuss some of the shortfalls of each and provide some recommendations on best approaches for a fast, efficient and highly concurrent solution.
  • Java Threads and Concurrent Locks with Examples
    In this tutorial we will focus primarily on using the concurrent utilities and how these can make concurrent programming easier for us.

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *