Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 5
The broad categories of messages (synchronous and asynchronous) can themselves be subdivided in various ways. For example, a balking message is one that can't even be initiated. Imagine that you could only open a limited number of database connections at a given moment, and that all the connections were in use. A message that required access to the database could "balk" if it couldn't get the connection. It isn't that it tried to do the operation and failed; rather, it couldn't even initiate the operation to give it a chance to fail.
Another variant on synchronous messages is a timeout. Rather than balking immediately, the method decides to wait for a predetermined amount of time for the resource to become available. If that time expires, the request will fail. (The operation probably never started, but if the operation indeed started, it certainly didn't complete successfully.) In Java, a read from a socket can timeout in this way.
The problem isn't design; it's implementation. Designing asynchronous systems in an object-oriented way isn't particularly difficult. Object-oriented-design notations such as UML (the "Universal Modeling Language") can easily capture notions such as synchronous and asynchronous messages. Implementing these notions in the essentially procedural system mandated by the Java threading model is another matter, however.
Given an object-oriented design perspective -- a network of objects communicating via messages -- what's the best way to implement an asynchronous message? The most naive way, which is workable in simple situations, is for each asynchronous-message handler to spawn its own thread.
First, let's consider the following synchronous method, which flushes an internal buffer out to a file. (The Reader_Writer lock was discussed last month.)
import com.holub.asynch.Reader_writer; import java.io.*; class Synchronous_flush { private final OutputStream out; private Reader_writer lock = new Reader_writer(); private byte[] buffer; private int length; public Synchronous_flush( OutputStream out ) { this.out = out; } //... synchronized void flush( ) throws IOException { try { lock.request_write(); out.write( buffer, 0, length ); length = 0; } finally { lock.write_accomplished(); } } }
This blocking version of flush() presents several problems. For one thing, flush() can block indefinitely while waiting to acquire the reader/writer lock. Moreover, if the OutputStream was a socket connection rather than a file, the write operation itself could take a long time to do. Finally, since flush() is synchronized, the entire object is locked while the flush is in progress, so any thread that tries to call any other synchronized method of Synchronous_flush will block until the flush() completes. This wait could turn into a nested-monitor-lockout situation should the lock not be released.
These problems can be solved by making flush() asynchronous; the flush() method should simply initiate the flush operation and then return immediately. Here's an initial (yet, as you'll soon see,
not very successful) attempt: