8.7 Solving the Race Condition

 < Day Day Up > 



8.7 Solving the Race Condition

The problem in Exhibit 5 (Program8.5) is that the path can be created and finished before the ball thread can execute a wait on it. To solve this timing dependency, the creation of the Path object and the wait must be inside of the same synchronized block in the run method. This ensures that the draw method is not run and that the notify cannot occur until the wait has been executed. This solution is shown in Exhibit 5 (Program8.5), which is a correct solution to this animation program. This procedure of creating a Path object, waiting in the run method, and coordinating with the GUI thread via a call to notify in the draw method can be used with the animator for any asynchronous object.

Exhibit 5: Program8.5: Correct Concurrent Animation

start example

 import java.awt.*; import java.util.*; import animator.*; /**  * This class animates a ball in a thread using the animator  * component. It is a correct solution.  */ public class ConcurrentBall implements DrawListener, Runnable {   static final int EAST = 0;   static final int WEST = 1;   static final int NORTH = 2;   static final int SOUTH = 3;   int direction;   Path myPath;   Random random;   Animator animator;   /**    * Constructor. Note that we need to register with the    * animator through the parent's class.    */   public ConcurrentBall(Animator animator, int direction) {     this.animator = animator;     this.direction = direction;     random = new Random(direction);   }   /**    * The run method simulates an asynchronous ball. The    * myPath variable is set here and used in the draw method    * and is intended to coordinate the ball thread running in    * this method and the GUI thread (from the animator)    * running in the draw method. This works correctly.    */   public void run() {     animator.addDrawListener(this);     try {       while(true) {         synchronized (this) {           if (direction = = SOUTH) {             myPath = new StraightLinePath(410, 205, 10, 205,                50);             direction = NORTH;           }           else if (direction = = NORTH) {             myPath = new StraightLinePath(10, 205, 410, 205,                50);             direction = SOUTH;           }           else if (direction = = EAST) {             myPath = new StraightLinePath(205, 410, 205, 10,                50);             direction = WEST;           }           else if (direction = = WEST) {             myPath = new StraightLinePath(205, 10, 205, 410,                50);             direction = EAST;           }           wait();         }       Thread.sleep(random.nextInt(10000));       }     } catch (InterruptedException e) {     }   }   /**    * Draw is called each time through the animator loop to draw    * the object. It simply uses the path to calculate the    * position of this object and then draws itself at that    * position. When the end of the path is reached, it notifies    * the ball thread.    */   public synchronized void draw(DrawEvent de) {     Point p = myPath.nextPosition();     Graphics g = de.getGraphics();     g.setColor(Color.red);     g.fillOval((int)p.getX(), (int)p.getY(), 15, 15);     if (! myPath.hasMoreSteps())       notify();   }   /**    * The main method just creates the animator and    * the ball threads and starts them running.    */   public static void main(String args[]) {     Animator animator = new Animator();     ConcurrentBall cb1 = new ConcurrentBall(animator, WEST);     (new Thread(cb1)).start();     ConcurrentBall cb2 = new ConcurrentBall(animator, NORTH);     (new Thread(cb2)).start();     animator.setVisible(true);   } } 

end example

While this method of simply putting a synchronized block around the path creation and the notify method works, it is somewhat confusing as it encompasses a fairly large section of the code in the method. Also, the steps, or procedure, of locking the code, creating the path, and doing a wait can occur many times in a program; simply copying the code in the program each time it occurs is time consuming, confusing, and prone to errors. Any time procedures occur more than once, the procedure is a good candidate to be abstracted for reuse. Because the procedure is abstracted, the type of reuse here is procedural reuse. The steps involved in moving the object are abstracted into a method named move, and the program is refactored to implement the move method. A move method is created in the class Car in Program8.7 (Exhibits 7 through 9) and is used to move the car as part of the animation.

Creating a move method is easy for a single object; however, abstracting out the behavior to apply it to multiple objects is not as easy as creating the method. Variables that are local to each object, such as the Path, must be manipulated in the move method, which means that a procedural function will be insufficient for a reusable move method. A stronger methodology is needed. Fortunately, two exist: classification and composition. These two methodologies are explained in Chapter 10.



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

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