Study guide: Achieve strong performance with threads, Part 4

Brush up on Java terms, learn tips and cautions, review homework assignments, and read Jeff's answers to student questions

Glossary of terms

fixed-delay execution
A kind of timer task execution where the execution of each task subsequent to the first task occurs relative to the previous task execution's actual execution time. Furthermore, if an execution delays because of garbage collection or some other background activity, all subsequent executions also delay.
fixed-rate execution
A kind of timer task execution where the execution of each task subsequent to the first task occurs relative to the first task execution's actual execution time. Furthermore, if an execution delays because of garbage collection or some other background activity, two or more executions occur in rapid succession to maintain the execution frequency.
inheritable thread-local variables
InheritableThreadLocal objects that store values on a per-thread basis. Furthermore, a parent thread's values are accessible to all child threads.
main memory
Object heap memory.
tasks
TimerTask and subclass objects.
thread groups
java.lang.ThreadGroup objects that group the Thread (and Thread subclass) objects of related threads.
thread-local variables
ThreadLocal objects that store values on a per-thread basis.
timer
An object that executes code either once or periodically, and either at some specified time or after a time interval.
volatility
Changeability.
working memory
Processor registers and cache.

Tips and cautions

These tips and cautions will help you write better programs and save you from agonizing over why the compiler produces error messages.

Tips

  • Once you no longer need a hierarchy of ThreadGroup objects, call ThreadGroup's void destroy() method via a reference to the ThreadGroup object at the top of that hierarchy. If the top ThreadGroup object and all subgroup objects lack thread objects, destroy() prepares those thread group objects for garbage collection. Otherwise, destroy() throws an IllegalThreadStateException object. However, until you nullify the reference to the top ThreadGroup object (assuming a field variable contains that reference), the garbage collector cannot collect that object. Referencing the top object, you can determine if a previous call was made to the destroy() method by calling ThreadGroup's boolean isDestroyed() method. That method returns true if the thread group hierarchy was destroyed.
  • You can easily determine a thread group's parent group by calling ThreadGroup's ThreadGroup getParent() method. For all thread groups, save system, this method returns a nonnull reference. For system, this method returns null. You can also find out if a thread group is the parent, grandparent, and so forth of another thread group by calling ThreadGroup's boolean parentOf(ThreadGroup tg) method. That method returns true if a thread, whose reference you use to call parentOf(ThreadGroup tg), is a parent (or other ancestor) of the group that tg references—or is the same group as the tg-referenced group. Otherwise, the method returns false.
  • To ensure that a read/write operation (outside a synchronized context) on either a long-integer shared field variable or a double-precision floating-point shared field variable succeeds, prefix the shared field variable's declaration with keyword volatile.
  • Override InheritableThreadLocal's childValue(Object parentvalue) method to make the child's inheritable thread-local value a function of the parent's inheritable thread-local value.
  • To terminate the currently running task without affecting other tasks, call TimerTask's boolean cancel() method. That method cancels the current task so that it will never run again (assuming the task is repeating) after it finishes its current execution and returns a Boolean true value if either the task is a one-time task that has not yet run or a repeating task. False returns if a one-time task has already run, if it was never scheduled, or if it was cancelled. TimerTask's cancel() method does not cancel any other tasks.

Cautions

  • The volatile and final keywords cannot appear together in a shared field variable declaration. Any attempt to include both keywords forces the compiler to report an error.
  • ThreadDeath is a powerful tool for causing a thread to terminate its execution. However, this tool is dangerous, and is the reason Sun deprecated the stop() method. When a thread throws a ThreadDeath object, all locked monitors unlock as ThreadDeath propagates up the method-call stack. Objects protected by these monitors become accessible to other threads. If those objects are in an inconsistent state, a program can experience erratic behavior, a database or file can corrupt, and so on. However, if you know that the thread is not holding any locks, you can safely throw ThreadDeath.

Homework

  • If you do not specify a thread group for a newly created thread, to which thread group does the thread belong?
  • Identify two uses for thread groups.
  • Can you destroy the system thread group?
  • Does the volatile keyword provide an alternative to synchronization?
  • Why can you not declare a shared field variable to be volatile and final?
  • Write a program that uses InheritableThreadLocal's childValue(Object parentvalue) method to compute a child thread's initial value, as a function of a parent thread's initial value, for the inheritable thread-local variable.
  • Write a program that uses the Timer and TimerTask classes to periodically run a pair of tasks. After one of those tasks runs five times, use TimerTask's cancel() method to cancel that task.
  • In the article's Clock1 application, replace Timer t = new Timer (); with Timer t = new Timer (true);. Recompile the source code and execute java Clock1. What happens and why?
  • Why does the reference handler thread have a higher priority than the finalizer thread?

Answers to last month's homework

Last month, I asked you complete the source code to the BB (bounded buffer) application by supplying source code to the Buffer class. I also told you to use the wait() and notify() methods. The source code to my version of the complete BB application follows:

// BB.java
class BB
{
   public static void main (String [] args)
   {
      Buffer buffer = new Buffer (5); // Buffer holds a maximum of 5 characters
      new Writer (buffer).start ();
      new Reader (buffer).start ();
   }
}
class Buffer
{
   private char [] buffer;
   private int inspos = -1;
   Buffer (int length)
   {
      buffer = new char [length];
   }
   public synchronized void put (char c)
   {
      if (isFull ())
         try
         {
             wait ();
         }
         catch (InterruptedException e)
         {
         }
      buffer [++inspos] = c;
      notify ();
   }
   public synchronized char get ()
   {
      if (isEmpty ())
         try
         {
             wait ();
         }
         catch (InterruptedException e)
         {
         }
      char c = buffer [0];
      System.arraycopy (buffer, 1, buffer, 0, inspos--);
      notify ();
      return c;
   }
   private boolean isEmpty ()
   {
      return (inspos == -1) ? true : false;
   }
   private boolean isFull ()
   {
      return (inspos == buffer.length - 1) ? true : false;
   }
}
class Writer extends Thread
{
   private Buffer buffer;
   Writer (Buffer buffer)
   {
      this.buffer = buffer;
   }
   public void run ()
   {
      for (int i = 0; i < 26; i++)
          buffer.put ((char) ('A' + i));
      for (int i = 0; i < 26; i++)
          buffer.put ((char) ('a' + i));
      for (int i = 0; i < 10; i++)
          buffer.put ((char) ('0' + i));
      buffer.put ('*'); // Indicate end of data items
   }
}
class Reader extends Thread
{
   private Buffer buffer;
   Reader (Buffer buffer)
   {
      this.buffer = buffer;
   }
   public void run ()
   {
      char c;
      while ((c = buffer.get ()) != '*')
         System.out.print (c);
      System.out.print ('\n');
   }
}