This tutorial provides an example of a very simple application that creates 2 threads. Each thread will perform the following actions:
The main purpose of this tutorial is to demonstrate how threads operate independently, and how race conditions can occur within multi-threaded applications.
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); } } }