1.4 Components

 < Day Day Up > 



1.4 Components

An interesting way to look at a concurrent program is to think of it as containing two types of units, activities that act on other entities or entities that control the interactions of these activities. If these units are objects, then in a concurrent program all objects in that program can be made to be either active (asynchronous activities such as threads) or passive (such as a shared resource or an event that is used for synchronization). Other types of simple, non-concurrent objects are used by active and passive objects, such as vectors or StringTokenizers, but these are not involved in the concurrency in the program.

Most programmers do not have problems understanding active objects, as they are simply instructions that are written and executed in a procedural order that, in principle, can be represented by a flow chart, nor do they have problems understanding non-concurrent objects. This is probably because the behavior of the object can normally be understood in the context of the activity within which it is being run, much like a procedural program. This is how students have been taught to program since their first introductory class.

However, passive objects, which from now on will be called concurrent components or simply components, are much more difficult for most programmers. This is likely because they provide the infrastructure for the asynchronous activities that executed in a concurrent program. This is a somewhat foreign concept to many programmers.

Components in the example of making a pie are the shared mixing bowl and the event that signifies that preparation of the crust and filling is completed. They control the behavior of the asynchronous activities so that they coordinate and produce a correct result. They also sit between asynchronous activities and are shared and used by multiple asynchronous activities.

Note that not all objects that are non-active are components. For example, a vector is safe to use in a multi-threaded program, but it is not a component because even if it is used by a number of threads it is not normally used to coordinate between those threads. Objects are added or removed from the vector, but the vector is used just to store data elements, not to coordinate the asynchronous activities. A special type of vector called a bounded buffer (presented in Chapter 3) is actually used to coordinate between asynchronous activities.

Because components provide an infrastructure for asynchronous activities and coordinate between these activities, they have a number of characteristics that must be considered that do not exist when implementing normal objects. Some of these characteristics are enumerated here:

  • Because components coordinate between several threads, they cannot be created or owned by a single thread; therefore, some mechanism must be used to allow these objects to be registered, or to register themselves, with other objects representing the asynchronous activities. Many mechanisms are available to do this, ranging from using a simple parameter in a constructor to special purpose methods in GUI components to entire protocols such as Light-Weight Directory Access Protocol (LDAP) for remote objects.

  • Because components are used in separate asynchronous activities and, in the extreme case of distributed computing, on physically different computers, some mechanism must be implemented to allow the components to communicate with the asynchronous activities. Once again, these mechanisms range from simple method invocation in the case of threads to entire protocols when distributed objects are used.

  • Unlike objects for asynchronous activities, which can be designed using procedural flow, the logic in a component is generally organized around the state of the component when it is executed. Some mechanism needs to be designed to effectively implement the components to allow them to provide this coordination (see Chapter 3).

  • Some harmful interactions, called race conditions, can occur if the objects are not properly designed. One way to avoid race conditions is to make all the methods in the object synchronized and not allow an object to give up the object's lock while it is executing. This is called complete synchronization and is sufficient for non-component objects such as a string or a vector; however, components must coordinate between several objects, and complete synchronization is too restrictive to effectively implement this coordination. Much of the rest of the book is concerned with how to safely relax the synchronized conditions.

  • A second type of harmful interaction, called a deadlock, can result if the component is not properly designed. Deadlock can occur in any concurrent program when objects are improperly handled; however, the possibility of deadlock can actually be built into components that are not designed properly, even if the component is used correctly. Several examples of deadlock are provided in the text, particularly in Chapter 7 on Java events.

Two examples are given here to show how these conditions affect a component. The first example of a component is a button object. A button provides a service to the GUI thread by relaying the occurrence of an event (the button has been pressed) to any other objects that are interested in this event (the Listeners). In Exhibit 1 (Program1.1), the button is created in the main thread and then passed to the GUI thread by adding it to the Frame object. It is then used by other threads that are interested in knowing when the button is pressed through the addActionListener methods in the button. Thus, the button is independent of the threads that use it (the GUI thread or any threads associated with the ActionListeners). So, the button is an independent object that provides a coordination service between multiple other threads as well as the service of informing other asynchronous activities (in this case, threads) that the button was pressed. A special mechanism, called an event, is used to allow the button to communicate with the threads with which it is interfacing. For this simple program, it is not necessary to worry about the state of the button or race or deadlock conditions, but the reasons why these could affect even a simple button are covered in detail in subsequent chapters.

Another example of components is most distributed services that use distributed objects such as RMI, Common Object Request Broker (CORBA), or Enterprise Java Beans (EJB). When using distributed objects, the components exist on centrally located servers and provide services to remote clients, such as a Web browser on a PC, which are processes or programs running on other computers and can access the components through a network. In the case of distributed programs, all five of the problems that can occur in components (listed above) are of vital importance, as will be seen in Chapter 13.



 < 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