Date: Wed, 25 Sep 1996 17:03:10 +0100
From: Richard Beton <rdb@roke.co.uk>
Organization: Roke Manor Research Ltd
To: phw <P.H.Welch@ukc.ac.uk>
Subject: Re: Java Workshop - Internal Visit Report
Status: RO


Author:  Richard Beton at roke-itn
Date:    25/09/96  15:42
Subject: Java Threads Workshop
------------------------------- Message Contents -------------------------------

     Here is a report on the key issues presented at the
     Java Threads Workshop in Canterbury, September 23-24 1996.


     IMPORTANT NOTE: This report was written for internal use at
		     Roke Manor Research Ltd and is copyright
		     Roke Manor Research Ltd.  It is made public
		     by their permission, but Roke Manor accept
		     no liability for any use to which it is put.


     Summary of the Programme
     ========================

     * Introduction

     * Tutorial: Java Basics (Paddy Nixon)

     * Lecture: Java Threads I (P Welch)

     * Lecture: Analysis & Design using OO (R van Rein)

     * Classroom: hands-on exercises and demonstrations

     * Lecture: Java Threads II (P Welch)

     * Presentation: A Channel Library (G Hilderink)

     * Discussion group workshops

     * Conclusions



     Java Threads
     ============

     Although most of the issues discussed were directly concerned with 
     programming multi-threaded Java appplications (and applets), the 
     principles could equally be applied to C++ multi-threaded programming, 
     or even programming with Posix Lightweight Threads on Unix etc.

     Java threads have a lifecycle state machine involving 5 essential 
     states:



                                   [NotStarted]
                                        |
                                        |
                                  start |
                                        |
                                        |
                                        v
                ------------------->
     [Runnable]                      [Running] --------------+
                <-------------------                         |
          ^            yield           |   |                 | 
          |                            |   |                 | 
          |                            |   |                 |
          |                      sleep |   | suspend         | stop 
          |                            |   |                 |
          |                            v   v                 v 
          |   resume
          +------------------------- [Blocked]           [Stopped]


     This is a diagram with some of the possible transitions omitted for 
     clarity. Runnable describes a thread which is on the execution queue, 
     but is awaiting time-slice access to the processor. Otherwise, threads 
     are essentially either Running or Blocked.

     The suspend() and resume() methods were discussed. As a rule of thumb, 
     they should never be used because they do not involve synchronisation 
     and can easily lead to deadlock (a thread can be infinitely 
     suspended).

     'Synchronized' methods ensure exclusive execution by only one thread 
     at a time. This enables the atomic modificaion of object state. This 
     is secure for use by any number of threads. Every Java object has a 
     monitor which provides this synchronisation between threads. Before 
     accessing a synchronized method, a thread has to wait in a queue to 
     access the monitor for that object, unless the monitor is free.

     The wait() and notify() methods also use the monitor. They are called 
     from within the synchronized block, i.e. after the monitor has been 
     grabbed. wait() puts the calling thread on a standby queue. notify() 
     moves the thread at the head of the standby queue (if any) to the back 
     of the monitor queue. It is quite easy to write code where an unlucky 
     thread moves from queue to queue and back again without ever getting 
     the resource it is seeking - this is 'starvation' and is usually 
     undesirable.

     A major consequence of the use of monitors is that synchronised 
     methods are difficult to understand, and impossible to understand in 
     isolation. Consider the CubbyHole class in the Javasoft Java 
     Tutorial:


     class CubbyHole
     {
         private int contents;
         private boolean available = false;

         public synchronized int get()
         {
             while (available == false)
             {
                 try
                 {
                     wait();
                 }
                 catch (InterruptedException e) {}
             }
             available = false;
             notify();
             return contents;
         }

         public synchronized void put(int value) 
         ...  body of put method
     }


     CubbyHole stores an integer; access is via the get() and put() 
     methods. Inspection of the get() method is not helpful. The while loop 
     appears to be infinite, because 'available' is not changed within it. 
     It is only possible to understand the get() method by also 
     understanding the put() method. The 'available' flag is actually 
     changed by a different thread whilst the getting thread is waiting, 
     but it's not easy to see that is this case (I have deliberately not 
     included the body of put() to emphasise the point - if you haven't 
     seen the example before, you can only guess at how it might work).

     This sort of coding is what makes multi-threaded programming hard to 
     do, and even harder to verify and maintain. Therefore, if we do it 
     this way, it's costing us MONEY! 

     The solution proposed at the workshop is to construct black-box 
     passive communication objects to do all the difficult stuff. Our 
     application consists only of active threads which communicate with 
     each other using passive communication objects. The active objects 
     need not (and should not) use the wait, notify, or synchronised 
     keywords at all.

     The passive objects require careful use of wait(), notify(), etc and 
     are very difficult to write correctly. Fortunately, these difficult 
     passive objects need only be implemented once. I have an example of 
     one for anyone interested.

     As an aside, note that this distinction between active and passive 
     objects is reminiscent of the similar Shlaer-Mellor distinction, except 
     that Shlaer-Mellor were only interested in passive data storage using 
     intelligent store objects, and they were not concerned with 
     communication between threads per se (theirs is an analysis 
     methodology).

     Note also that the passive communications objects are essentially 
     similar to channels in the occam sense - not surprising, since the 
     lecture presenters have been working with the theory behind channel 
     communication for over a decade.

     As an example, consider the following code fragments. Within some 
     'main()' method, two threads and a channel are instantiated and 
     connected:

         ...  blah
         Channel chan = new Channel();
         WriterThread a = new WriterThread (chan); 
         ReaderThread b = new ReaderThread (chan); 
         ...  more blah

     The Channel has public methods 
     'void write(Object o)' and
     'Object read()'
     So within the WriterThread, anything can be passed to the 
     ReaderThread, for example a WriterThread method may contain:

         ...  blah
         String string = "Hello World";
         chan.write (string);
         ...  more blah

     and at the reading end, within some ReaderThread method:

         ...  blah
         String tellMe = chan.read();
         ...  more blah


     N.B. Java provides run-time type checking: an exception is raised if 
     chan.read() does not receive a String (in this case). As per usual in 
     Java, the instanceof operator would allow code to test the received 
     type and cast it safely.



     Java Recommendation
     ===================

     * For the reasons explained above, abandon the use of the following 
       primitives: synchronized, wait(), notify(), suspend(), resume().

     * Consider your application threads to be ACTIVE.

     * Apply carefully honed PASSIVE channel classes to the task of
       synchronising and communicating between ACTIVE threads.

     See me for further details of ready-made channel classes. Also I have 
     plenty of information concerning the construction of programs which 
     you can be sure will never deadlock. If you don't reason carefully 
     about deadlock freedom, then your multi-threaded applications can, in 
     all probability, deadlock at some time or other. There is now a 
     substantial body of knowledge concerning how to prevent deadlock.



     Performance Issues
     ==================

     Java monitors in the current release (JDK1.0.2) are known to be 
     dreadfully slow. This is likely to improve in the future, but how much 
     it improves is impossible to predict. Occam compilers, such as the Kent 
     University KROC compiler, produce code for which similar thread 
     scheduling and synchronisation operates three orders of
     magnitude faster (yes, 1000 times!).


     Rick

              Richard Beton BSc MInstP CPhys
      Roke Manor Research, Romsey, Hampshire SO51 0ZN
-------  Standard disclaimer about my own views etc  -------
           See http://www1.roke.co.uk/WHR/WHR.html

--------------687D380021B

--------------------------------------------------------------------------------


