Home > Articles > Software Development & Management > Object Technology

  • Print
  • + Share This

1.2 Objects and Concurrency

There are many ways to characterize objects, concurrency, and their relationships. This section discusses several different perspectives — definitional, system-based, stylistic, and modeling-based — that together help establish a conceptual basis for concurrent object-oriented programming.

1.2.1 Concurrency

Like most computing terms, "concurrency" is tricky to pin down. Informally, a concurrent program is one that does more than one thing at a time. For example, a web browser may be simultaneously performing an HTTP GET request to get an HTML page, playing an audio clip, displaying the number of bytes received of some image, and engaging in an advisory dialog with a user. However, this simultaneity is sometimes an illusion. On some computer systems these different activities might indeed be performed by different CPUs. But on other systems they are all performed by a single time-shared CPU that switches among different activities quickly enough that they appear to be simultaneous, or at least nondeterministically interleaved, to human observers.

A more precise, though not very interesting definition of concurrent programming can be phrased operationally: A Java virtual machine and its underlying operating system (OS) provide mappings from apparent simultaneity to physical parallelism (via multiple CPUs), or lack thereof, by allowing independent activities to proceed in parallel when possible and desirable, and otherwise by time-sharing. Concurrent programming consists of using programming constructs that are mapped in this way. Concurrent programming in the Java programming language entails using Java programming language constructs to this effect, as opposed to system-level constructs that are used to create new operating system processes. By convention, this notion is further restricted to constructs affecting a single JVM, as opposed to distributed programming, for example using remote method invocation (RMI), that involves multiple JVMs residing on multiple computer systems.

Concurrency and the reasons for employing it are better captured by considering the nature of a few common types of concurrent applications:

    Web services. Most socket-based web services (for example, HTTP daemons, servlet engines, and application servers) are multithreaded. Usually, the main motivation for supporting multiple concurrent connections is to ensure that new incoming connections do not need to wait out completion of others. This generally minimizes service latencies and improves availability.

    Number crunching. Many computation-intensive tasks can be parallelized, and thus execute more quickly if multiple CPUs are present. Here the goal is to maximize throughput by exploiting parallelism.

    I/O processing. Even on a nominally sequential computer, devices that perform reads and writes on disks, wires, etc., operate independently of the CPU. Concurrent programs can use the time otherwise wasted waiting for slow I/O, and can thus make more efficient use of a computer's resources.

    Simulation. Concurrent programs can simulate physical objects with independent autonomous behaviors that are hard to capture in purely sequential programs.

    GUI-based applications. Even though most user interfaces are intentionally single-threaded, they often establish or communicate with multithreaded services. Concurrency enables user controls to stay responsive even during time-consuming actions.

    Component-based software. Large-granularity software components (for example those providing design tools such as layout editors) may internally construct threads in order to assist in bookkeeping, provide multimedia support, achieve greater autonomy, or improve performance.

    Mobile code. Frameworks such as the java.applet package execute downloaded code in separate threads as one part of a set of policies that help to isolate, monitor, and control the effects of unknown code.

    Embedded systems. Most programs running on small dedicated devices perform real-time control. Multiple components each continuously react to external inputs from sensors or other devices, and produce external outputs in a timely manner. As defined in The Java Language Specification, the Java platform does not support hard real-time control in which system correctness depends on actions being performed by certain deadlines. Particular run-time systems may provide the stronger guarantees required in some safety-critical hard-real-time systems. But all JVM implementations support soft real-time control, in which timeliness and performance are considered quality-of-service issues rather than correctness issues (see 1.3.3). This reflects portability goals that enable the JVM to be implemented on modern opportunistic, multipurpose hardware and system software.

1.2.2 Concurrent Execution Constructs

Threads are only one of several constructs available for concurrently executing code. The idea of generating a new activity can be mapped to any of several abstractions along a granularity continuum reflecting trade-offs of autonomy versus overhead. Thread-based designs do not always provide the best solution to a given concurrency problem. Selection of one of the alternatives discussed below can provide either more or less security, protection, fault-tolerance, and administrative control, with either more or less associated overhead. Differences among these options (and their associated programming support constructs) impact design strategies more than do any of the details surrounding each one. Computer systems

If you had a large supply of computer systems, you might map each logical unit of execution to a different computer. Each computer system may be a uniprocessor, a multiprocessor, or even a cluster of machines administered as a single unit and sharing a common operating system. This provides unbounded autonomy and independence. Each system can be administered and controlled separately from all the others.

However, constructing, locating, reclaiming, and passing messages among such entities can be expensive, opportunities for sharing local resources are eliminated, and solutions to problems surrounding naming, security, fault-tolerance, recovery, and reachability are all relatively heavy in comparison with those seen in concurrent programs. So this mapping choice is typically applied only for those aspects of a system that intrinsically require a distributed solution. And even here, all but the tiniest embedded computer devices host more than one process. Processes

A process is an operating-system abstraction that allows one computer system to support many units of execution. Each process typically represents a separate running program; for example, an executing JVM. Like the notion of a "computer system", a "process" is a logical abstraction, not a physical one. So, for example, bindings from processes to CPUs may vary dynamically.

Operating systems guarantee some degree of independence, lack of interference, and security among concurrently executing processes. Processes are generally not allowed to access one another's storage locations (although there are usually some exceptions), and must instead communicate via interprocess communication facilities such as pipes. Most systems make at least best-effort promises about how processes will be created and scheduled. This nearly always entails pre-emptive time-slicing — suspending processes on a periodic basis to give other processes a chance to run.

The overhead for creating, managing, and communicating among processes can be a lot lower than in per-machine solutions. However, since processes share underlying computational resources (CPUs, memory, IO channels, and so on), they are less autonomous. For example, a machine crash caused by one process kills all processes.

Figure 1-7 Threads

Thread constructs of various forms make further trade-offs in autonomy, in part for the sake of lower overhead. The main trade-offs are:

    Sharing. Threads may share access to the memory, open files, and other resources associated with a single process. Threads in the Java programming language may share all such resources. Some operating systems also support intermediate constructions, for example "lightweight processes" and "kernel threads" that share only some resources, do so only upon explicit request, or impose other restrictions.

    Scheduling. Independence guarantees may be weakened to support cheaper scheduling policies. At one extreme, all threads can be treated together as a single-threaded process, in which case they may cooperatively contend with each other so that only one thread is running at a time, without giving any other thread a chance to run until it blocks (see 1.3.2). At the other extreme, the underlying scheduler can allow all threads in a system to contend directly with each other via pre-emptive scheduling rules. Threads in the Java programming language may be scheduled using any policy lying at or anywhere between these extremes.

    Communication. Systems interact via communication across wires or wireless channels, for example using sockets. Processes may also communicate in this fashion, but may also use lighter mechanisms such as pipes and interprocess signalling facilities. Threads can use all of these options, plus other cheaper strategies relying on access to memory locations accessible across multiple threads, and employing memory-based synchronization facilities such as locks and waiting and notification mechanisms. These constructs support more efficient communication, but sometimes incur the expense of greater complexity and consequently greater potential for programming error. Tasks and lightweight executable frameworks

The trade-offs made in supporting threads cover a wide range of applications, but are not always perfectly matched to the needs of a given activity. While performance details differ across platforms, the overhead in creating a thread is still significantly greater than the cheapest (but least independent) way to invoke a block of code — calling it directly in the current thread.

When thread creation and management overhead become performance concerns, you may be able to make additional trade-offs in autonomy by creating your own lighter-weight execution frameworks that impose further restrictions on usage (for example by forbidding use of certain forms of blocking), or make fewer scheduling guarantees, or restrict synchronization and communication to a more limited set of choices. As discussed in 4.1.4, these tasks can then be mapped to threads in about the same way that threads are mapped to processes and computer systems.

The most familiar lightweight executable frameworks are event-based systems and subsystems (see 1.2.3, 3.6.4, and 4.1), in which calls triggering conceptually asynchronous activities are maintained as events that may be queued and processed by background threads. Several additional lightweight executable frameworks are described in Chapter 4. When they apply, construction and use of such frameworks can improve both the structure and performance of concurrent programs. Their use reduces concerns (see 1.3.3) that can otherwise inhibit the use of concurrent execution techniques for expressing logically asynchronous activities and logically autonomous objects (see 1.2.4).

1.2.3 Concurrency and OO Programming

Objects and concurrency have been linked since the earliest days of each. The first concurrent OO programming language (created circa 1966), Simula, was also the first OO language, and was among the first concurrent languages. Simula's initial OO and concurrency constructs were somewhat primitive and awkward. For example, concurrency was based around coroutines — thread-like constructs requiring that programmers explicitly hand off control from one task to another. Several other languages providing both concurrency and OO constructs followed — indeed, even some of the earliest prototype versions of C++ included a few concurrency-support library classes. And Ada (although, in its first versions, scarcely an OO language) helped bring concurrent programming out from the world of specialized, low-level languages and systems.

OO design played no practical role in the multithreaded systems programming practices emerging in the 1970s. And concurrency played no practical role in the wide-scale embrace of OO programming that began in the 1980s. But interest in OO concurrency stayed alive in research laboratories and advanced development groups, and has re-emerged as an essential aspect of programming in part due to the popularity and ubiquity of the Java platform.

Concurrent OO programming shares most features with programming of any kind. But it differs in critical ways from the kinds of programming you may be most familiar with, as discussed below. Sequential OO programming

Concurrent OO programs are often structured using the same programming techniques and design patterns as sequential OO programs (see for example 1.4). But they are intrinsically more complex. When more than one activity can occur at a time, program execution is necessarily nondeterministic. Code may execute in surprising orders — any order that is not explicitly ruled out is allowed (see 2.2.7). So you cannot always understand concurrent programs by sequentially reading through their code. For example, without further precautions, a field set to one value in one line of code may have a different value (due to the actions of some other concurrent activity) by the time the next line of code is executed. Dealing with this and other forms of interference often introduces the need for a bit more rigor and a more conservative outlook on design. Event-based programming

Some concurrent programming techniques have much in common with those in event frameworks employed in GUI toolkits supported by java.awt and javax.swing, and in other languages such as Tcl/Tk and Visual Basic. In GUI frameworks, events such as mouse clicks are encapsulated as Event objects that are placed in a single EventQueue. These events are then dispatched and processed one-by-one in a single event loop, which normally runs as a separate thread. As discussed in 4.1, this design can be extended to support additional concurrency by (among other tactics) creating multiple event loop threads, each concurrently processing events, or even dispatching each event in a separate thread. Again, this opens up new design possibilities, but also introduces new concerns about interference and coordination among concurrent activities. Concurrent systems programming

Object-oriented concurrent programming differs from multithreaded systems programming in languages such as C mainly due to the encapsulation, modularity, extensibility, security, and safety features otherwise lacking in C. Additionally, concurrency support is built into the Java programming language, rather than supplied by libraries. This eliminates the possibility of some common errors, and also enables compilers to automatically and safely perform some optimizations that would need to be performed manually in C.

While concurrency support constructs in the Java programming language are generally similar to those in the standard POSIX pthreads library and related packages typically used in C, there are some important differences, especially in the details of waiting and notification (see 3.2.2). It is very possible to use utility classes that act almost just like POSIX routines (see 3.4.4). But it is often more productive instead to make minor adjustments to exploit the versions that the language directly supports. Other concurrent programming languages

Essentially all concurrent programming languages are, at some level, equivalent, if only in the sense that all concurrent languages are widely believed not to have defined the right concurrency features. However, it is not all that hard to make programs in one language look almost equivalent to those in other languages or those using other constructs, by developing packages, classes, utilities, tools, and coding conventions that mimic features built into others. In the course of this book, constructions are introduced that provide the capabilities and programming styles of semaphore-based systems ( 3.4.1), futures ( 4.3.3), barrier-based parallelism ( 4.4.3), CSP ( 4.5.1) and others. It is a perfectly great idea to write programs using only one of these styles, if this suits your needs. However, many concurrent designs, patterns, frameworks, and systems have eclectic heritages and steal good ideas from anywhere they can.

1.2.4 Object Models and Mappings

Conceptions of objects often differ across sequential versus concurrent OO programming, and even across different styles of concurrent OO programming. Contemplation of the underlying object models and mappings can reveal the nature of differences among programming styles hinted at in the previous section.

Most people like to think of software objects as models of real objects, represented with some arbitrary degree of precision. The notion of "real" is of course in the eye of the beholder, and often includes artifices that make sense only within the realm of computation.

For a simple example, consider the skeletal UML class diagram and code sketch for class WaterTank:

Figure 1-8

class WaterTank {                // Code sketch
 final float capacity;
 float currentVolume = 0.0f;
 WaterTank overflow;
 WaterTank(float cap) { capacity = cap; ... }
 void addWater(float amount) throws OverflowException;
 void removeWater(float amount) throws UnderflowException;

The intent here is to represent, or simulate, a water tank with:

  • Attributes, such as capacity and currentVolume, that are represented as fields of WaterTank objects. We can choose only those attributes that we happen to care about in some set of usage contexts. For example, while all real water tanks have locations, shapes, colors, and so on, this class only deals with volumes.

  • Invariant state constraints, such as the facts that the currentVolume always remains between zero and capacity, and that capacity is nonnegative and never changes after construction.

  • Operations describing behaviors such as those to addWater and removeWater. This choice of operations again reflects some implicit design decisions concerning accuracy, granularity and precision. For example, we could have chosen to model water tanks at the level of valves and switches, and could have modeled each water molecule as an object that changes location as the result of the associated operations.

  • Connections (and potential connections) to other objects with which objects communicate, such as pipes or other tanks. For example, excess water encountered in an addWater operation could be shunted to an overflow tank that is known by each tank.

  • Preconditions and postconditions on the effects of operations, such as rules stating that it is impossible to remove water from an empty tank, or to add water to a full tank that is not equipped with an available overflow tank.

  • Protocols constraining when and how messages (operation requests) are processed. For example, we may impose a rule that at most one addWater or removeWater message is processed at any given time or, alternatively, a rule stating that removeWater messages are allowed in the midst of addWater operations.

Figure 1-9 Object models

The WaterTank class uses objects to model reality. Object models provide rules and frameworks for defining objects more generally, covering:

    Statics. The structure of each object is described (normally via a class) in terms of internal attributes (state), connections to other objects, local (internal) methods, and methods or ports for accepting messages from other objects.

    Encapsulation. Objects have membranes separating their insides and outsides. Internal state can be directly modified only by the object itself. (We ignore for now language features that allow this rule to be broken.)

    Communication. Objects communicate only via message passing. Objects issue messages that trigger actions in other objects. The forms of these messages may range from simple procedural calls to those transported via arbitrary communication protocols.

    Identity. New objects can be constructed at any time (subject to system resource constraints) by any object (subject to access control). Once constructed, each object maintains a unique identity that persists over its lifetime.

    Connections. One object can send messages to others if it knows their identities. Some models rely on channel identities rather than or in addition to object identities. Abstractly, a channel is a vehicle for passing messages. Two objects that share a channel may pass messages through that channel without knowing each other's identities. Typical OO models and languages rely on object-based primitives for direct method invocations, channel-based abstractions for IO and communication across wires, and constructions such as event channels that may be viewed from either perspective.

    Computation. Objects may perform four basic kinds of computation:

    • Accept a message.

    • Update internal state.

    • Send a message.

    • Create a new object.

This abstract characterization can be interpreted and refined in several ways. For example, one way to implement a WaterTank object is to build a tiny special-purpose hardware device that only maintains the indicated states, instructions, and connections. But since this is not a book on hardware design, we'll ignore such options and restrict attention to software-based alternatives. Sequential mappings

The features of an ordinary general-purpose computer (a CPU, a bus, some memory, and some IO ports) can be exploited so that this computer can pretend it is any object, for example a WaterTank. This can be arranged by loading a description of WaterTanks (via a .class file) into a JVM. The JVM can then construct a passive representation of an instance and then interpret the associated operations. This mapping strategy also applies at the level of the CPU when operations are compiled into native code rather than interpreted as bytecodes. It also extends to programs involving many objects of different classes, each loaded and instantiated as needed, by having the JVM at all times record the identity ("this") of the object it is currently simulating.

Figure 1-10

In other words, the JVM is itself an object, although a very special one that can pretend it is any other object. (More formally, it serves as a Universal Turing Machine.) While similar remarks hold for the mappings used in most other languages, Class objects and reflection make it simpler to characterize reflective objects that treat other objects as data.

In a purely sequential environment, this is the end of the story. But before moving on, consider the restrictions on the generic object model imposed by this mapping. On a sequential JVM, it would be impossible to directly simulate multiple concurrent interacting waterTank objects. And because all message-passing is performed via sequential procedural invocation, there is no need for rules about whether multiple messages may be processed concurrently — they never are anyway. Thus, sequential OO processing limits the kinds of high-level design concerns you are allowed to express. Active objects

At the other end of the mapping spectrum are active object models (also known as actor models), in which every object is autonomous. Each may be as powerful as a sequential JVM. Internal class and object representations may take the same forms as those used in passive frameworks. For example here, each waterTank could be mapped to a separate active object by loading in a description to a separate JVM, and then forever allowing it to simulate the defined actions.

Figure 1-11

Active object models form a common high-level view of objects in distributed object-oriented systems: Different objects may reside on different machines, so the location and administrative domain of an object are often important programming issues. All message passing is arranged via remote communication (for example via sockets) that may obey any of a number of protocols, including oneway messaging (i.e., messages that do not intrinsically require replies), multicasts (simultaneously sending the same message to multiple recipients), and procedure-style request-reply exchanges.

This model also serves as an object-oriented view of most operating-system-level processes, each of which is as independent of, and shares as few resources with, other processes as possible (see 1.2.2). Mixed models

The models and mappings underlying concurrency support in the Java programming language fall between the two extremes of passive and active models. A full JVM may be composed of multiple threads, each of which acts in about the same way as a single sequential JVM. However, unlike pure active objects, all of these threads may share access to the same set of underlying passive representations.

Figure 1-12

This style of mapping can simulate each of the extremes. Purely passive sequential models can be programmed using only one thread. Purely active models can be programmed by creating as many threads as there are active objects, avoiding situations in which more than one thread can access a given passive representation (see 2.3), and using constructs that provide the same semantic effects as remote message passing (see 4.1). However, most concurrent programs occupy a middle ground.

Thread-based concurrent OO models conceptually separate "normal" passive objects from active objects (threads). But the passive objects typically display thread-awareness not seen in sequential programming, for example by protecting themselves via locks. And the active objects are simpler than those seen in actor models, supporting only a few operations (such as run). But the design of concurrent OO systems can be approached from either of these two directions — by smartening up passive objects to live in a multithreaded environment, or by dumbing down active objects so they can be expressed more easily using thread constructs.

One reason for supporting this kind of object model is that it maps in a straightforward and efficient way to stock uniprocessor and shared-memory multiprocessor (SMP) hardware and operating systems: Threads can be bound to CPUs when possible and desirable, and otherwise time-shared; local thread state maps to registers and CPUs; and shared object representations map to shared main memory.

Figure 1-13

The degree of programmer control over these mappings is one distinction separating many forms of parallel programming from concurrent programming. Classic parallel programming involves explicit design steps to map threads, tasks, or processes, as well as data, to physical processors and their local stores. Concurrent programming leaves most mapping decisions to the JVM (and the underlying OS). This enhances portability, at the expense of needing to accommodate differences in the quality of implementation of these mappings.

Time-sharing is accomplished by applying the same kind of mapping strategy to threads themselves: Representations of Thread objects are maintained, and a scheduler arranges context switches in which the CPU state corresponding to one thread is saved in its associated storage representation and restored from another.

Several further refinements and extensions of such models and mappings are possible. For example, persistent object applications and systems typically rely on databases to maintain object representations rather than directly relying on main memory.

1.2.5 Further Readings

There is a substantial literature on concurrency, ranging from works on theoretical foundations to practical guides for using particular concurrent applications. Concurrent programming

Textbooks presenting details on additional concurrent algorithms, programming strategies, and formal methods not covered in this book include:

    Andrews, Gregory. Foundations of Multithreaded, Parallel, and Distributed Programming, Addison-Wesley, 1999. This is an expanded update of Andrews's Concurrent Programming: Principles and Practice, Benjamin Cummings, 1991.

    Ben-Ari, M. Principles of Concurrent and Distributed Programming, Prentice Hall, 1990.

    Bernstein, Arthur, and Philip Lewis. Concurrency in Programming and Database Systems, Jones and Bartlett, 1993.

    Burns, Alan, and Geoff Davis. Concurrent Programming, Addison-Wesley, 1993.

    Bustard, David, John Elder, and Jim Welsh. Concurrent Program Structures, Prentice Hall, 1988.

    Schneider, Fred. On Concurrent Programming, Springer-Verlag, 1997.

The concurrency constructs found in the Java programming language have their roots in similar constructs first described by C. A. R. Hoare and Per Brinch Hansen. See papers by them and others in following collections:

    Dahl, Ole-Johan, Edsger Dijkstra, and C. A. R. Hoare (eds.). Structured Programming, Academic Press, 1972.

    Gehani, Narain, and Andrew McGettrick (eds.). Concurrent Programming, Addison-Wesley, 1988.

A comparative survey of how some of these constructs are defined and supported across different languages and systems may be found in:

    Buhr, Peter, Michel Fortier, and Michael Coffin. "Monitor Classification", ACM Computing Surveys, 1995.

Concurrent object-oriented, object-based or module-based languages include Simula, Modula-3, Mesa, Ada, Orca, Sather, and Euclid. More information on these languages can be found in their manuals, as well as in:

    Birtwistle, Graham, Ole-Johan Dahl, Bjorn Myhrtag, and Kristen Nygaard. Simula Begin, Auerbach Press, 1973.

    Burns, Alan, and Andrew Wellings. Concurrency in Ada, Cambridge University Press, 1995.

    Holt, R. C. Concurrent Euclid, the Unix System, and Tunis, Addison-Wesley, 1983.

    Nelson, Greg (ed.). Systems Programming with Modula-3, Prentice Hall, 1991.

    Stoutamire, David, and Stephen Omohundro. The Sather/pSather 1.1 Specification, Technical Report, University of California at Berkeley, 1996.

Books taking different approaches to concurrency in the Java programming language include:

    Hartley, Stephen. Concurrent Programming using Java, Oxford University Press, 1998. This takes an operating systems approach to concurrency.

    Holub, Allen. Taming Java Threads, Apress, 1999. This collects the author's columns on threads in the JavaWorld online magazine.

    Lewis, Bil. Multithreaded Programming in Java, Prentice Hall, 1999. This presents a somewhat lighter treatment of several topics discussed in this book, and provides closer tie-ins with POSIX threads.

    Magee, Jeff, and Jeff Kramer. Concurrency: State Models and Java Programs, Wiley, 1999. This provides a stronger emphasis on modeling and analysis.

Most books, articles, and manuals on systems programming using threads concentrate on the details of those on particular operating systems or thread packages. See:

    Butenhof, David. Programming with POSIX Threads, Addison-Wesley, 1997. This provides the most complete discussions of the POSIX thread library and how to use it.

    Lewis, Bil, and Daniel Berg. Multithreaded Programming with Pthreads, Prentice Hall, 1998.

    Norton, Scott, and Mark Dipasquale. Thread Time, Prentice Hall, 1997.

Most texts on operating systems and systems programming describe the design and construction of underlying support mechanisms for language-level thread and synchronization constructs. See, for example:

    Hanson, David. C Interfaces and Implementations, Addison-Wesley, 1996.

    Silberschatz, Avi and Peter Galvin. Operating Systems Concepts, Addison-Wesley, 1994.

    Tanenbaum, Andrew. Modern Operating Systems, Prentice Hall, 1992. Models

Given the diverse forms of concurrency seen in software, it's not surprising that there have been a large number of approaches to the basic theory of concurrency. Theoretical accounts of process calculi, event structures, linear logic, Petri nets, and temporal logic have potential relevance to the understanding of concurrent OO systems. For overviews of most approaches to the theory of concurrency, see:

    van Leeuwen, Jan (ed.). Handbook of Theoretical Computer Science, Volume B, MIT Press, 1990.

An eclectic (and still fresh-sounding) presentation of models, associated programming techniques, and design patterns, illustrated using diverse languages and systems, is:

    Filman, Robert, and Daniel Friedman. Coordinated Computing. McGraw-Hill, 1984.

There are several experimental concurrent OO languages based on active objects, most notably the family of Actor languages. See:

    Agha, Gul. ACTORS: A Model of Concurrent Computation in Distributed Systems, MIT Press, 1986.

A more extensive survey of object-oriented approaches to concurrency can be found in:

    Briot, Jean-Pierre, Rachid Guerraoui, and Klaus-Peter Lohr. "Concurrency and Distribution in Object-Oriented Programming", Computing Surveys, 1998.

Research papers on object-oriented models, systems and languages can be found in proceedings of OO conferences including ECOOP, OOPSLA, COOTS, TOOLS, and ISCOPE, as well as concurrency conferences such as CONCUR and journals such as IEEE Concurrency. Also, the following collections contain chapters surveying many approaches and issues:

    Agha, Gul, Peter Wegner, and Aki Yonezawa (eds.). Research Directions in Concurrent Object-Oriented Programming, MIT Press, 1993.

    Briot, Jean-Pierre, Jean-Marc Geib and Akinori Yonezawa (eds.). Object Based Parallel and Distributed Computing, LNCS 1107, Springer Verlag, 1996.

    Guerraoui, Rachid, Oscar Nierstrasz, and Michel Riveill (eds.). Object-Based Distributed Processing, LNCS 791, Springer-Verlag, 1993.

    Nierstrasz, Oscar, and Dennis Tsichritzis (eds.). Object-Oriented Software Composition, Prentice Hall, 1995. Distributed systems

Texts on distributed algorithms, protocols, and system design include:

    Barbosa, Valmir. An Introduction to Distributed Algorithms. Morgan Kaufman, 1996.

    Birman, Kenneth and Robbert von Renesse. Reliable Distributed Computing with the Isis Toolkit, IEEE Press, 1994.

    Coulouris, George, Jean Dollimore, and Tim Kindberg. Distributed Systems: Concepts and Design, Addison-Wesley, 1994.

    Lynch, Nancy. Distributed Algorithms, Morgan Kaufman, 1996.

    Mullender, Sape (ed.), Distributed Systems, Addison-Wesley, 1993.

    Raynal, Michel. Distributed Algorithms and Protocols, Wiley, 1988.

For details about distributed programming using RMI, see:

    Arnold, Ken, Bryan O'Sullivan, Robert Scheifler, Jim Waldo, and Ann Wollrath. The Jini Specification, Addison-Wesley, 1999. Real-time programming

Most texts on real-time programming focus on hard real-time systems in which, for the sake of correctness, certain activities must be performed within certain time constraints. The Java programming language does not supply primitives that provide such guarantees, so this book does not cover deadline scheduling, priority assignment algorithms, and related concerns. Sources on real-time design include:

    Burns, Alan, and Andy Wellings. Real-Time Systems and Programming Languages, Addison-Wesley, 1997. This book illustrates real-time programming in Ada, occam, and C, and includes a recommended account of priority inversion problems and solutions.

    Gomaa, Hassan. Software Design Methods for Concurrent and Real-Time Systems, Addison-Wesley, 1993.

    Levi, Shem-Tov and Ashok Agrawala. Real-Time System Design, McGraw-Hill, 1990.

    Selic, Bran, Garth Gullekson, and Paul Ward. Real-Time Object-Oriented Modeling, Wiley, 1995.

  • + Share This
  • 🔖 Save To Your Account

Related Resources

There are currently no related titles. Please check back later.