Flylib.com

Books Software

 
 
 

Chapter 8: Cooperative Synchronization

 < Day Day Up > 



Chapter 8: Cooperative Synchronization

8.1 Introduction

Synchronization is used to coordinate asynchronous activities, such as threads. Up to this point in the book, synchronization of asynchronous activities has always been on shared resources; this is called competitive synchronization , because the activities are always competing for the shared resource. However, at times activities must synchronize on an event that must occur before all threads can continue; this type of synchronization is called cooperative synchronization . This chapter explains what cooperative synchronization is and why it is important, as well as some of the pitfalls that can occur when implementing it.

The animator presented in Chapter 7 is used to demonstrate visually cooperative synchronization. All of the examples using the animator in Chapter 7 had objects that were created in the main thread, and then the animator simply called draw methods in these objects. The objects did not run in threads themselves but allowed their draw methods to be called in the GUI thread; therefore, they could only implement actions in the GUI thread and could not run independently from the GUI thread. In the examples in this chapter, the objects that are animated will implement threads, which requires that the threads cooperate with the GUI thread to implement the correct behavior.

A number of the same problems encountered in competitive synchronization will also be present in cooperative synchronization. Too little synchronization will result in race conditions and activities that do not cooperate correctly, while too much synchronization will result in activities that either deadlock or have performance problems. As always, care must be taken to implement an adequate amount of synchronization to solve the problem safely, but not so much that it causes liveness concerns.



 < Day Day Up > 

 < Day Day Up > 



8.2 Chapter Goals

After completing this chapter, you should know the following:

  • The difference between cooperative synchronization and competitive synchronization

  • What can happen if threads are not coordinated and how to use the synchronization with wait and notify methods to achieve cooperative synchronization

  • Why it is invalid to use sleep times to achieve cooperative synchronization

  • How to protect against race conditions when using the animator to move an object in a thread

  • What is a notification object and how it can be implemented



 < Day Day Up > 

 < Day Day Up > 



8.3 Cooperative and Competitive Synchronization

Synchronization is used in a program for two reasons: allow threads to compete for a resource or allow them to coordinate activities. These two concepts were introduced in Chapter 1 in terms of baking a pie. Competitive synchronization in this case occurred when a shared resource (the bowl in which the pie crust and filling were to be mixed) was used. Cooperative synchronization occurred when two or more asynchronous activities had to wait for each other for a specific event to occur. So far in the text, only competitive synchronization has been covered, but this chapter introduces cooperative synchronization.

To understand how cooperative synchronization can occur in a problem we will use the animator from Chapter 7. The problem that illustrates cooperative synchronization is when a thread wishes to wait until the animator has finished moving an object before continuing to execute. To see how this problem occurs, consider Exhibit 1 (Program8.1), [1] which implements a simple animation of two threads that wait a random amount of time and then move a ball across the screen. The logic in the run method simply creates a path for the ball to travel, to the EAST if it was previously going WEST, to the NORTH if it was going SOUTH, etc. The thread then goes to sleep while the GUI thread calls the draw method for the ball, which moves the ball. The draw method uses the Path variable that was created in the run method to control the steps in the animation. When the ball reaches its destination, when the path has no more steps, it will then simply wait until the thread is done sleeping and reverse its direction and move again.

Exhibit 1: Program8.1: Thread Animation with No Coordination

start example

import java.awt.*; import java.util.*; import animator.*; /** * This class animates a ball in a thread using the animator * component but fails because the thread is not coordinated * with the GUI thread calling the draw method. */ 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. Just save information for this ball. */ public ConcurrentBall(Animator animator, int direction) { this.direction = direction; random = new Random(direction); this.animator = animator; } /** * 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. But, no coordination is in * place, and this approach fails. */ public void run() { animator.addDrawListener(this); try { while(true) { 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; } 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. */ public 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); } /** * 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

Threads were used to make this program sleep a random amount of time between movement, as the sleep could be made part of the steps in the activity. This behavior of inserting a random sleep into the thread cannot be easily accomplished unless the balls are in threads. (A more complex example using the gas station simulation from Chapter 3 is presented later; utilizing sleep as part of the problem solution is easy to implement, as the cars will be in separate threads.)

When this program is run and the balls are moving quickly, the solution in Exhibit 1 (Program8.1) appears to be correct; however, as with everything else regarding asynchronous activities, we cannot test the program to prove it is correct. Care must be taken to implement the program correctly or the results may not be as expected. To see this, run the animation and pull the speed control all the way to the left, causing the program to go at its slowest speed. A disturbing behavior then becomes apparent. The ball moves some portion of the way across the screen, suddenly jumps to the far side of the screen, and begins moving in the opposite direction.

The reason the ball jumps to the other side of the screen is that the ball thread wakes up before the GUI thread has finished moving the ball to the other side of the screen. When the ball thread wakes up it creates a new path object and the draw method now uses this new path object immediately, thus moving the ball to the other side of the screen where this new path begins. This is not the intended behavior. The problem here is that the GUI thread and the ball thread are uncoordinated. The ball thread should actually suspend until the GUI thread has completed moving the ball across the screen and then should begin its sleep.

Making the program behave so that the ball thread does not start to sleep until the movement of the ball is completed is an example of cooperative synchronization. Somehow these two threads must be coordinated. How to coordinate these threads (i.e., how to achieve cooperative synchronization) is the topic of the remainder of this chapter. Once these techniques have been covered, they are combined in Chapter 9 with the competitive synchronization techniques already discussed. As will be seen, combining these techniques allows for the development of very powerful and extensible programs.

[1] Note that the animations in this chapter all use the animator component developed in Chapter 8 and stored in the animator package that is available with this book. The useAnimator.bat file will correctly set up all the directories needed to run these programs.



 < Day Day Up >