HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Java's support for concurrency is sufficient enough to achieve a wide range of desired results. While the primitives provided are very powerful, they can also be easily misused and may lead to unpredictable behavior. It is well known that in a multithreaded environment, due to the lack of mature tools available, the debugging process could easily modify the state of the program being debugged.

It is unreasonable to expect all Java programmers to be experts in concurrent programming. Deciding if and when to use the concurrency mechanism is a problem in itself, but once the decision is made, a new and fundamental question arises: How could I be sure, that the correctness of my program will not be adversely affected? It is obvious that adding synchronization constructs to a sequential design is a completely wrong approach. One possible solution is to build higher level synchronization abstractions (e.g. rendezvous classes). But, as the old saying goes, when you do not know how to drive well, it does not matter how powerful the car is. It might be even more dangerous!

The approach taken in this paper is based on the process of understanding Design Patterns using a new feature like inner class from Java 1.1 Some of the samples will implement basic patterns like Waiter Threads and Early Reply Threads others will only suggest ways of thinking. It might be worthwhile to take the time to express some of the solutions at the level of patterns. The correctness of the programs with multiple possible behaviors called non-determinate will not depend on how the threads are interleaved by the scheduler on different platforms. Rather, it will be an expression of the correct implementation of a well known pattern. The simplest way to avoid worries that one day on one machine your program will encounter a deadlock or a starvation state is to follow the form of a well-proven designed pattern in a multithreaded environment.

Introduction
In the old days of non-concurrency object-oriented programming, we dealt with two personages: the Client who wants an activity to be performed and the Server containing the code to perform the activity. The personages are still alive! The Client may say: "I need to perform this activity and you, Server, could help me. I give you the context and part of my well being (read object state) and please help me". The Server tries, while the Client waits, and finally responds, "I did it, my health is normal, and your well being was not affected". Everyone is happy: we as programmers and our objects. But, there is another possible scenario. The Server responds: "I encounter this exception, do not count on my results and be careful, your health will be affected". And the conversation goes on, but at least we could easily trace the relationships between our little creatures. For a better understanding, sometimes I use interaction diagrams [1]. For those of you who are not used to such diagrams, here is a little bit of terminology.

To express the dynamic interactions between objects, a symbolism is introduced which depicts situations in object relationships. In the form x.message(arg), which is the mechanism of Java invocation, the object bound to variable x is called the target object. When the server tries to use other services acting as a client, we call it the host. The nodes represent forms of existence of an object and the evolution of relationship is represented by links sometimes annotated with a message. The interaction diagram gives us an intuitive image of protocols at the level of the object relationship.

So far, the timing of the execution was not a factor in our considerations. The Client can wait as long as the Server needs to complete its job. But here is the problem: What if the Client wants to perform another activity during the period in which Server tries its best to satisfy the request? It is obvious we need another personage: Thread. So, what is a thread in our object world? It is an object that has the ability to perform in an asynchronous mode. What does it mean? The following are the easier roles of a thread.

  1. When the Client wants to call the Server, the Thread says, "Use me and you will be free to do whatever you want". So the Client builds a little help object with its request and gives this object to Thread. In pattern terminology, we have a Waiter Thread.
  2. When the Server receives a request from Client, the Thread says, "Here I am Server, I know how to do it, use me and you, Server, can take other requests from Client. This type of relationship is defined as an Early Reply Thread model.
Basics
There are few basic constructs in Java for concurrency mechanism: the class java.lang.Thread; the keywords: synchronized and volatile, the methods defined in java.lang.Object: wait, notify, notifyAll. On the other hand, they generate a wide range of coding practice and design strategies. We define a Runnable as an object which is the instance of a class implementing Runnable interface. Since Thread class implements Runnable interface, every instance of at least a Thread class will be a Runnable. The run method of Thread class is defined as:

public void run() { if (target != null) target.run(); }

where target is a Runnable reference in constructor form Thread(Runnable target). There is an interesting point that should be mentioned: Any object can access the methods of a Thread class finding out the thread it is running within using the Thread static method: Thread.currentThread(). Thus, any Runnable can use methods defined in the Thread class without itself having to be a thread. Why is this important? Because it helps us in the process of debugging in the old-fashioned style using print statement. There are methods in the Thread class that are very useful for identifying the threads like: getName() or toString() and so on. The run method of a Runnable could be written as:

public void run() {
Thread runningThread = Thread.currentThread();
System.out.println("Born of: " + runningThread.toString());
...
System.out.println("Death of: " + runningThread.getName());
}

Sometimes it is important to know where the born of a thread occurs invoking start method or if the thread dies from natural causes (run method exits) all at a level of a Runnable in constructs of the form: class WhateverClass extends WhateverSuperClass implements Runnable.

As you know, JDK 1.1 is extended. One of the most useful features is the concept of inner classes. It helps us to implement some patterns in a natural way. An inner class is a class defined as a member of another class. The inner class can use the members of enclosing class and is not usable outside its scope. Because the inner class needs to determine the enclosing instance, the compiler adds an extra private member to the inner class constructor (Listing 1). Some confusion can arise from the combination of lexical scoping and inheritance. For example, if we have:

class A { int x=1; }
class B extends A { int x=2;
//inner class
class C { int x = 3; int sum() { return x + B.this.x +
((A)B.this).x; } }
}

what is the result of new B().new C().sum()? [6]. Let us examine the terms of sum: B.this.x is the value x in enclosing class B and ((A)B.this).x is the value of x in A. Since the language prohibits an inner class from having the same name as any of its enclosing classes the current instance can be referred to by qualifying the keyword 'this' like in expression B.this.x. Java allows inherited names to hide the ones defined in enclosing scope, but prohibits them from being used without explicit qualification. We can subclass an inner class C like:

class D extends B.C { int x = 4; B b;
//ctr must have at least one arg.
//an enclosing instance of class C which is B
D(B b) {b.super(); this.b = b;}
int sum() { return x + super.x + b.x + ((A)b).x; }
}

How could a subclass D of an inner class C be built without knowing the enclosing instance of the inner class? The answer is that such a subclass cannot be built, unless the constructor of D has an argument of type reference to enclosing instance of inner C. The first statement of the constructor must be an explicit constructor invocation qualifying the keyword 'super'. The value 10 is the result of an invocation of type new D(new B()).sum(). The code snippet shows how we could access hidden instances with qualifying names. The right expression of the method sum in D would be: int sum() { return x + super.sum(); } Any current instance of an inner class, other than the instance of inner class itself, is called 'enclosing instance' of an inner class. By qualifying the keyword 'new' we can specify explicitly the enclosing instance:

class Outer {
class Inner { ... }
Inner innerObj = this.new Inner();
}

There are many aspects not mentioned here, like anonymous class and how the synchronization is applied in the context of inner classes, but one fundamental question must be answered: Why do we need the inner class in the first place? Have you thought about how the callback mechanism could be implemented in Java? The natural way would be to have a feature like inner class where a construct like 'method pointer' could be built as we soon see the implementation of some patterns.

Fundamentals
The most difficult part in the process of reusable software is to abstract forms of organization. If a pattern describes a design form, it means that by having a pattern we not only reuse components, but more importantly, the interactions between components at a higher level of flow of organization. As an example, I will define and implement a type of Waiter Thread pattern called Operation. What is the Waiter Thread Pattern? It is a model where the thread is constructed by a client and the server method could be executed asynchronous. It should be applied when waiting or blocking are not the right options for a client which wants to perform other activities. If we think of an Operation as applying an operator to an operand, we see how easy is to define a pattern of type Waiters. The unary operator of primitive type in Java[3] should be a model of an abstract unary operator which could be applied to a reference type. Let's try to design such an operator having its operand type as an abstract inner class (see Listing 2).

Defining an Operator in this form has some advantages. The Operator is closely related to its operand, it could inform the client (operation) at least about its success/failure and, more importantly, it is a Runnable.

How should an Operation pattern of client type look? For implementing the callback mechanism required by the server, it is clear that a runnable inner class should be defined and a start type method must be included for launching the operation. So a class Operation would look like Listing 3.

As an example, let us suppose that we define an operator called Show and an operand called URL (Listing 4). Applying the operator Show to operand URL gives us the operation ShowURL. The implementation of Operation pattern for this particular case is shown in Listing 5.

Why is ShowURL a type of Waiter Thread pattern? Because the client ShowURL builds a thread and calls the server to apply the operator Show to the operand URL. If we want to execute the operation ShowURL with String url = "http://www.javadevelopersjournal.com", the invocation is:

new ShowURL(url).start();

Thus, the invocation of the form

new OperatorOperand(arg).start()

is an operation with intricacies resembling the invocation of a unary operator ++ to operand j in expression ++j.

Conclusions
The next and natural question would be: Why do we need such an Operation pattern when there is a simple way, a direct approach? Because what we created can be applied to whatever operations you think of: Read/WriteFile, RenderImage, DrawForm and so on. The preferred method is a matter of choice, but in the world of reusable components it would be nice to have well-defined patterns which could be used or extended. I did not approach the synchronization technique that will be tackled in my next article. The pattern could be granularly refined at level of sending and receiving data. Thinking back to the inner classes, I remember my wise teacher G. Auslander, mentioning Einstein's words, "Many times I realize how much my own outer and inner life is built upon the efforts of my fellow men, and how earnestly I must exert myself in order to give in return as much as I have received". I hope this introduction helped.

References

  1. Doug Lea "Concurrent Programming in Java Design Principles and Patterns" Addison-Wesley 1997.
  2. E. Gamma, R. Helm, R. Johnson, J. Vlissides "Design Patterns" Addison-Wesley 1995.
  3. K. Arnold, J. Gosling "The Java Programming Language" Addison-Wesley 1996.
  4. Brian Maso, "Thread Synchronization in Java" Java Developer's Journal 1996.
About the Author
Jordan Anastasiade holds a BS in Architecture and MS in Mathematics. He works for Hummingbird Communications Ltd., focusing on design patterns using object-oriented techniques in Java. Jordan can be reached at jordan@hummingbird.com

	

Listing 1

/* FILE: Outer.java */
/** Lexical scoping sample */	

class Supper { int x = 0; }

public class Outer extends Supper {
  protected int x;
  protected Inner innerObj;
  private String outerStr = " Outer val: ";

  Outer(int x) {
    this.x = x;
    innerObj = this.new Inner(x + 1);
  }
	
  protected class Inner {
    protected int x;
    protected String innerStr = " Inner val: ";

    Inner(int x) { this.x = x; }

    public String toString() {
      return ((Supper)Outer.this).x +
                outerStr + Outer.this.x +
                innerStr + x;
    }
  }

  public String toString() { 
    return innerObj.toString();
  }

  public static void main(String args[]) {
    Outer o = new Outer(1);
    System.out.println("Test Supper: " + o);
  }
}
//Result: Test Supper: 0 Outer val: 1 Inner val: 2

/* How it works ?   The bytecode might look like:
public class Outer {
  //...
  class Inner {
    private Outer this$0;//saved copy of Outer.this
    Outer$Inner(Outer this$0) {//Inner's ctr.
      this.this$0 = this$0;
    }
public void method() {//Outer's method
    Inner innerObj = new Outer$Inner(this);
*/

Listing 2

	abstract class Operator implements Runnable {
//message obj for callback client
	  private Runnable success, failure; 
//type of operand inner class
	  abstract protected class Operand { } 
	  abstract protected boolean operatorImp();
	  protected synchronized void operator() 
{ if (operatorImp()) success.run(); ... }
	  public void run() { operator(); }

Listing 3

	abstract class Operation {
//action in case of success
	  abstract protected void successfulOperation(); 
//inner class as Runnable
	  protected class Success implements Runnable { 
	    public void run() { successfulOperation(); }
	  }
	  abstract public void start();
	} 

Listing 4

/* FILE: Show.java */	
import java.net.*;
import java.io.*;
/**  Model for an Unary Operator class */
abstract class Operator implements Runnable {
  private Runnable success;
  private Runnable failure;

  /** Operand: an abstract protected inner class  */
  abstract protected class Operand { }

  public Operator(Runnable s, Runnable f) {
    success = s; failure = f;
  }

  // placeholder for operator implementation 
  abstract protected boolean operatorImpl();

  protected synchronized void operator() {
    if (operatorImpl()) success.run();
    else failure.run();
  }
  public void run() { operator(); }
}

/**  Sample of a server: Operator called Show */
public class Show extends Operator {
  DefOperand someURL; //operator's operand
  protected class DefOperand 
            extends Operator.Operand {
    protected boolean result = true;	
    protected URL url;
    //constructor for inner class
    public DefOperand(String urlString) {
      try {
        url = new URL(urlString);
      } catch (MalformedURLException e) {
        result = false;
      }
    }
  }
  public Show(String str, Runnable s, Runnable f){
    super(s, f);
    //for this operator define an new operand
    someURL = this.new DefOperand(str);
  }
  //particular operator's method implementation
  protected boolean operatorImpl() {
    String line;
    if (someURL.result) {
      try {
        URLConnection c = 
           someURL.url.openConnection();
        DataInputStream dis =
           new DataInputStream(c.getInputStream());
        //method readLine has been deprecated: J1.1
        while ((line = dis.readLine()) != null) 
          System.out.println(line);
        dis.close();
      } catch (IOException ioe) {
         someURL.result = false;
      }
    }
    return someURL.result;
  }
}

/* FILE: ShowURL.java */
/** Model for a Waiter Pattern: Operation class */
abstract class Operation {

  abstract protected void succesfulOperation();
  abstract protected void failedOperation();

  protected class Success implements Runnable {
    public void run() {succesfulOperation();}
  }

  protected class Failure implements Runnable {
    public void run() {failedOperation();}
  }
  abstract public void start(); 
}

/** Sample of Operation called ShowURL */
public class ShowURL extends Operation {
  protected String urlString;
  public ShowURL(String s) { urlString = s; }

  protected void succesfulOperation() {
    System.out.println("Success: " + urlString);
  }
  protected void failedOperation() {
    System.out.println("Failure: " + urlString);
  }

  public void start() {
    Runnable s = new Success();
    Runnable f = new Failure();
    new Thread(new Show(urlString, s, f)).start();
  }

  public static void  main(String args[]) {
    String url = "http://www.javadevelopersjournal.com";
    new ShowURL(url).start();
  }
}
// Result: ... html Success: http://www.javadevelopersjournal.com

Listing 5

	public class ShowURL extends Operation {
	...
	  public void start() {
	    Runnable s = new Success();
	    Runnable f = new Failure();
	    new Thread(new Show(urlString, s, f)).start();
	  }


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.