Creating Threads with the Runnable Interface

   

Java™ 2 Primer Plus
By Steven Haines, Steve Potts

Table of Contents
Chapter 18.  Programming with Threads


The laws of Java prohibit extending more than one class at a time, which is called multiple inheritance. Other languages such as C++ allow this, but there are logical conundrums that the authors of Java wanted to avoid.

The way that the Java creators chose to avoid the multiple inheritance problem is by allowing an unlimited number of interfaces to be implemented, but at most one class.

Interfaces are like classes that have no implementation. They normally define a set of methods that must be created in the child class to compile. The fact that these interfaces contain no code makes it impossible to create the conundrums that multiple inheritance can create.

You might wonder what good interfaces are, if they contain no code. They provide a standard set of methods that must be present. As a result, when your class interacts with another class, the other class can bet on your class having at least a trivial version of every method in the specification. You don't have to actually place meaningful code in each implemented method, however. This circumvents the problems that occur when another class, or the JVM, calls a method that doesn't exist.

The primary difference between a program that uses the Runnable interface and one that uses the Thread class is syntactic, as shown in Listing 18.4.

Listing 18.4 The TestRunnable1.java File
 /*   * TestRunnable1.java   *   * Created on September 25, 2002, 12:08 PM   */  package ch18;  /**   *   * @author  Stephen Potts   */  public class TestRunnable1 implements Runnable  {      /** Creates a new instance of TestRunnable */      public TestRunnable1()      {      }      public void run()      {          for ( int i=0; i<10; i++)          {              System.out.println("Hello from the new thread");          }          Thread t = Thread.currentThread();          System.out.println("The Thread name is " + t.getName());      }      public static void main(String[] args)      {          TestRunnable1 tr1 = new TestRunnable1();          Thread t = new Thread(tr1);          t.start();          for (int i=1; i<10; i++)          {              System.out.println("Hello from the main thread");          }      }  } 

Instead of extending the Thread class, we implement the Runnable interface.

 public class TestRunnable1 implements Runnable 

This interface contains only one method signature, the run() method.

The other difference is in how we instantiate this class. We declare an instance of this class, as always, but we can't start() it because it is not a Thread.

 TestRunnable1 tr1 = new TestRunnable1(); 

Instead, we create our own Thread instance. Notice that a handle to this class is passed in as a parameter.

 Thread t = new Thread(tr1); 

Finally, we call the start method using the handle to this new Thread object.

 t.start(); 

The output from this program is identical to the output from the Threaded version.

 Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the main thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  Hello from the new thread  The Thread name is Thread-1 

Notice that the currentThread() method still worked and allowed us to obtain the name of the thread.

Interrupting a Thread

Sometimes logic of a program dictates that a sleeping thread be interrupted. For example, you might put a thread to sleep when a queue is empty. When another process observes that the queue has new items in it, it could interrupt the sleeping thread and cause the thread to resume its processing.

The syntax of the interrupt method is simple. All that you have to do is call the interrupt() method on the sleeping thread and an InterruptedException is thrown inside the block where the sleep() method was called. Listing 18.5 shows an example of how to make this work.

Listing 18.5 The TestInterrupt.java File
 /*   * TestInterrupt.java   *   * Created on September 25, 2002, 12:08 PM   */  package ch18;  /**   *   * @author  Stephen Potts   */  public class TestInterrupt extends Thread  {      /** Creates a new instance of TestInterrupt */      public TestInterrupt()      {      }      public void run()      {          try          {              for ( int i=0; i<5; i++)              {                  System.out.println("running the first loop " + i);              }              Thread.sleep(10000);              for ( int i=6; i<10; i++)              {                  System.out.println("running the second loop" + i);              }          }catch (InterruptedException ie)          {              System.out.println("Sleep interrupted in run()");              for ( int i=11; i<15; i++)              {                  System.out.println("running the third loop" + i);              }          }      }      public static void main(String[] args)      {          TestInterrupt ti = new TestInterrupt();          Thread t = new Thread(ti);           t.start();          //Delay for a few seconds to let the other thread get going          try          {              Thread.sleep(2500);          }catch (InterruptedException ie)          {              System.out.println("Sleep interrupted in main()");          }          System.out.println("About to wake up the other thread");          t.interrupt();          System.out.println("Exiting from Main");      }  } 

In the run() method, we run one loop, and then go to sleep for 10 seconds.

 for ( int i=0; i<5; i++)  {      System.out.println("running the first loop " + i);  }  Thread.sleep(10000); 

If we awaken on our own, then we will execute this loop.

 for ( int i=6; i<10; i++)  {      System.out.println("running the second loop" + i);  } 

If we are interrupted, we execute this block instead.

 }catch (InterruptedException ie)  {      System.out.println("Sleep interrupted in run()");      for ( int i=11; i<15; i++)      {          System.out.println("running the third loop" + i);      } 

In the main() method, we delay for a few seconds, then wake up.

 //Delay for a few seconds to let the other thread get going      Thread.sleep(2500); 

Then, we print a warning and interrupt the thread.

 System.out.println("About to wake up the other thread");  t.interrupt(); 

Finally, we print an exit message.

 System.out.println("Exiting from Main"); 

The result of running this is shown here:

 running the first loop 0  running the first loop 1  running the first loop 2  running the first loop 3  running the first loop 4  About to wake up the other thread  Sleep interrupted in run()  running the third loop11  running the third loop12  running the third loop13  running the third loop14  Exiting from Main 

Notice that the second loop never executed. This is because the thread was put to sleep for 10 seconds, but it was interrupted after only 2.5 seconds. Notice also that the interruption of the thread allowed it to get control of the CPU before the main() finished.

Stopping a Thread

Prior to JDK 1.2, it was permissible to stop a thread at any time by issuing the stop() command. This approach was deemed unreliable because it led to corrupted objects in some circumstances. As a result of these problems, the stop() method was deprecated in JDK 1.2 and later.

This doesn't mean that you cannot stop a thread from running; it just means that you need to find a way to allow the program to terminate gracefully.

Listing 18.6 The TestStop.java File
 /*   * TestStop.java   *   * Created on September 25, 2002, 12:08 PM   */  package ch18;  /**   *   * @author  Stephen Potts   */  public class TestStop extends Thread  {      private volatile boolean stopping;      private Thread secondThread;       /** Creates a new instance of TestStop */      public TestStop()      {      }      public void run()      {          secondThread = Thread.currentThread();          stopping = false;          int counter = 0;          while( !stopping)          {              System.out.println("counter = " + counter);              counter++;              try              {                  Thread.sleep(1000);              }catch (InterruptedException ie)              {                  System.out.println("Sleep interrupted in run()");              }          }      }      public void stopIt()      {          this.stopping = true;      }      public static void main(String[] args)      {          TestStop ts = new TestStop();          Thread t = new Thread(ts);          t.start();          //Delay for a few seconds to let the other thread get going          try          {              Thread.sleep(2500);          }catch (InterruptedException ie)          {              System.out.println("Sleep interrupted in main()");          }          System.out.println("About to stop the other thread");          ts.stopIt();          System.out.println("Exiting from Main");      }  } 

The basic strategy for the termination of a thread is to allow the thread to proceed to the end of the run() method and terminate automatically. This eliminates all the problems associated with object corruption.

The first thing that we need is a boolean variable to hold the current state of the request for a stop.

 private volatile boolean stopping; 

The volatile keyword is a warning to the JVM that this variable can be changed from outside the currently running thread. This causes the JVM to turn off caching of local variables and look for this value every time that it is needed.

Tip

graphics/01icon19.gif

The volatile keyword is a favorite question on the various Java Certification Tests.


Next, we do all our work inside a while loop so that we can check the value of the stopping boolean regularly.

 while( !stopping)  {      System.out.println("counter = " + counter);      counter++;      try      {          Thread.sleep(1000);      }catch (InterruptedException ie)      {          System.out.println("Sleep interrupted in run()");      }  } 

When the stopping boolean is set to false, we simply drop out of the loop and the run method ends naturally.

 public void stopIt()  {      this.stopping = true;  } 

Whenever the program logic decides that the other thread needs to be stopped, a call to the stopIt() method will make that happen.

 ts.stopIt(); 

The result of running this program is shown here:

 counter = 0  counter = 1  counter = 2  About to stop the other thread  Exiting from Main 

Notice that nothing unnatural has taken place. This natural flow is the key to avoiding corrupt data.


       
    Top
     



    Java 2 Primer Plus
    Java 2 Primer Plus
    ISBN: 0672324156
    EAN: 2147483647
    Year: 2001
    Pages: 332

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net