Please send questions to st10@humboldt.edu .
/**
 * ThreadDemo1
 * 
 * modified and adapted from "Java Examples in a Nutshell", SECOND
 * edition, Flanagan, O'Reilly, pp. 71-72, in Example 4-1,
 * ThreadDemo.java
 *
 * (comments in quotes are directly quoted from Flanagan's 
 * ThreadDemo.java)
 *
 * "This class demonstrates the use of threads. The main() method
 * is the initial method invoked by the interpreter. It defines and
 * starts two more threads and the three threads run at the same time.
 * Note that this class extends Thread and overrides its run() method.
 * That method provides the body of one of the threads started by the
 * main() method."
 *
 * modified by: Sharon M. Tuttle
 * last modified: 3-12-01
 **/

public class ThreadDemo1 extends Thread
{
	/**
     	 * "This method overrides the run() method of Thread. It 
     	 * provides the body of this thread."
     	 * (this is the run() that thread1 below will do)
     	**/
	public void run()
	{
		for (int i=0; i<5; i++)
		{
			// see method compute() below, in this file
			compute();
		}
	}

	/**
	 * "This main method creates and starts two threads in addition 
	 * to the initial thread that the interpreter creates to invoke
	 * the main() method."
	 **/
	public static void main(String args[])
	{
		ThreadDemo1 thread1;     // a thread that is an instance of
					 // this class that will be created
					 // here;
		Thread	    thread2;     // a thread that will be created
					 // using the Thread constructor;

		// "Create the first thread: an instance of this class.
		// Its body is the run() method above."
		thread1 = new ThreadDemo1();
		thread1.setName("thread1");     // setting its name

		// "Create the second thread by passing a Runnable object
		// to the Thread constructor. The body of this thread is
		// the run() method of the anonymous Runnable object below."
		// [the second argument after the anonymous Runnable object
		// is setting the name of this thread)
		thread2 = new Thread(
				new Runnable()
				{
					public void run()
					{
						for (int i=0; i<10; i++)
						{
							compute();
						}
					}
				}, "thread2"
			);

		// "Set the priorities of these two threads, if any
		// are specified."
		if ((args != null) && (args.length >= 1))
		{
			try
			{
				thread1.setPriority(Integer.parseInt(args[0]));
			}
			catch (NumberFormatException exc)
			{
				System.err.println("Ignoring erroneous priority: " +
				     args[0] + "\n and starting thread1 with" +
				     " no specified priority");
			}
			catch (IllegalArgumentException exc)
			{
				System.err.println("Requested thread1 priority of " +
				     args[0] + " is more than maximum " + 
    				     "permitted\n priority of thread1's " +
				     "thread group: " + Thread.MAX_PRIORITY);
			}

			// only if there was 1 arg might there be 2...!
			if (args.length >= 2)
			{
				try
				{
					thread2.setPriority(Integer.parseInt(args[1]));
				}
				catch (NumberFormatException exc)
				{
					System.err.println("Ignoring erroneous priority: " +
					     args[1] + "\n and starting thread2 with" +
					     " no specified priority");
				}
				catch (IllegalArgumentException exc)
				{
					System.err.println("Requested thread2 priority of " +
					     args[1] + " is more than maximum " + 
    					     "permitted\n priority of thread2's " +
					     "thread group: " + Thread.MAX_PRIORITY);
				}
			}		
		}

		// "Start the two threads running"
		thread1.start();
		thread2.start();

		// "This main() method is run by the initial thread
		// created by the Java interpreter. Now that thread
		// does some stuff, too."
		for (int i=0; i<3; i++)
		{
			compute();
		}

		// "We could wait for the threads to stop running
		// with these lines. But they aren't necessary here,
		// so we won't bother."
		// try
		// {
		// 		thread1.join();
		//		thread2.join();
		// }
		// catch (InterruptedException exc)
		// {}

		// "The Java VM exits only when the main() method returns,
		// and when all threads stop running (except for daemon
		// threads --- see setDaemon())."
	}

	// "ThreadLocal objects [added as of Java 1.2!] represent a
	// value accessed with get() and set(). But they maintain a
	// different value for each thread. This object keeps track of
	// how many times each thread has called compute()."
	static ThreadLocal numCalls = new ThreadLocal();

	/** "This is the dummy method our threads all call." 
         **/
	static synchronized void compute()
	{
		Integer	n; 	// number of times compute() has been called
				// by current thread

		// "Figure out how many times we've been called by the
		// current thread."
		n = (Integer) numCalls.get();
		if (n == null)	// numCalls has no object associated with it,
				// initially;
		{
			n = new Integer(1);
		}
		else
		{
			n = new Integer(n.intValue() + 1);
		}

		// now set a new value for this numCalls object;
		numCalls.set(n);

		// "Display the name of the thread, and the number of
		// times [compute() has been] called [by that thread]"
		System.out.println(Thread.currentThread().getName() +
			": " + n);

		// "Do a long computation, simulating a "compute-bound"
		// thread"
		for (int i=0, j=0; i < 1000000; i++)
		{
			j += i;
		}

		// "Alternatively, we can simulate a thread subject to
		// network or I/O delays by causing it to sleep for a random
		// amount of time:"
		try
		{
			// "Stop running for a random number of milliseconds"
			// [note: sleep() NEEDS to be in a try-catch block!]
			Thread.sleep((int)(Math.random()*1000 + 1));
		}
		catch (InterruptedException exc)
		{}

		// "Each thread politely offers the other threads a chance
		// to run. This is important so that a compute-bound thread
		// does not "starve" other threads of equal priority."
		Thread.yield();
	}
}