Simple Thread Tutorial

This tutorial provides an example of a very simple application that creates 2 threads. Each thread will perform the following actions:

  1. Wait a random amount of time (up to 1 seconds).
  2. Update a shared variable with a thread-dependent value.
  3. Wait a random amount of time (up to 1 second).
  4. Print out the value of the shared variable.

The main purpose of this tutorial is to demonstrate how threads operate independently, and how race conditions can occur within multi-threaded applications.

SimpleThread.java

package edu.rutgers.sakai.java.thread;

import java.util.Random;

/**
 * A class which demonstrates how to create threads and how race conditions
 * occur.  It will spawn 2 threads which will modify a shared variable after a random
 * amount of time.  The threads will then wait another random period and print out the
 * value of the variable that they see.  Multiple executions of the main method
 * should result in different values of {@code sharedInteger} seen by each
 * Thread.
 * @author Robert Moore
 *
 */
public class SimpleThread {
	
	/**
	 * A shared variable that will be modified and printed by each thread.  Because
	 * a 32-bit int value is an atomic operation on 32-bit architectures, we don't
	 * need to worry about corrupted data, as we would with larger data types.
	 * We make it {@code volatile} to ensure that the threads don't cache
	 * its value in their local memory.
	 */
	public static volatile int sharedInteger = 0;
	
	/**
	 * A shared random number generator for all of the threads.
	 */
	public static final Random rand = new Random(System.currentTimeMillis());
	
	public static void main(String[]args){
		// Create our two racing threads
		RaceThread thread1 = new RaceThread(1);
		RaceThread thread2 = new RaceThread(2);
		
		// Start the two threads.  These methods will spawn new threads
		// and so will not "block" the current thread.
		thread1.start();
		thread2.start();
		
		// By joining, this thread (the main thread) waits for the other
		// threads to complete (exit run()).  These methods will block
		// the current thread until the RaceThreads are done.
		
		// The try-catch blocks are in case another thread or the VM "interrupts"
		// the current thread.  This happens rarely, but is possible.
		try {
			thread1.join();
		}catch(InterruptedException ie){
			System.err.println("May not have joined with thread1.");
		}
		try {
			thread2.join();
		}catch(InterruptedException ie){
			System.err.println("May not have joined with thread2.");
		}
	}
	
	/**
	 * An internal class used by SimpleThread.  It is kept private since it does
	 * not need to be used outside SimpleThread.  It is static so we don't need to
	 * create an instance of SimpleThread for each instance of RaceThread.
	 * @author Robert Moore
	 */
	private static class RaceThread extends Thread{
		
		/**
		 * The value that this RaceThread will assign to the shared variable.
		 */
		private final int number;
		
		/**
		 * Creates a new RaceThread with a number to assign to the shared
		 * variable.  This number should be unique among all threads being created.
		 * @param number the number to assign the shared variable.
		 */
		public RaceThread(final int number){
			super(Integer.toString(number));
			this.number = number;
		}
		
		/**
		 * The method that will be called when {@code RaceThread.start()} is invoked
		 * in {@code main(String[])}.  Basically it will print out its unique number,
		 * wait a random period of time up to 1 second, assign its number to the shared
		 * variable, wait a random period of time up to 1 second, and then print out the
		 * value of the shared variable.
		 */
		@Override
		public void run(){
			// Notify the user that the thread is started
			System.out.println(this.getName() + ": Starting up…");
			
			// Pick a random time to sleep (wait)
			try {
				Thread.sleep(rand.nextInt(1000));
			}catch(InterruptedException ie){
				// Ignored
			}
			// Modify the shared variable
			SimpleThread.sharedInteger = this.number;
			
			// Wait another random time
			try {
				Thread.sleep(rand.nextInt(1000));
			}catch(InterruptedException ie){
				// Ignored
			}
			// Print out what the shared variable looks like
			System.out.println(this.getName() + ": " + SimpleThread.sharedInteger);
		}
	}
}